diff options
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding')
197 files changed, 9969 insertions, 4921 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js index d39b2affd3..4945d33b23 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import LicenseModelActionHelper from './licenseModel/LicenseModelActionHelper.js'; import LicenseAgreementActionHelper from './licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; import FeatureGroupsActionHelper from './licenseModel/featureGroups/FeatureGroupsActionHelper.js'; @@ -29,9 +24,12 @@ import SoftwareProductNetworksActionHelper from './softwareProduct/networks/Soft import SoftwareProductComponentsActionHelper from './softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductComponentProcessesActionHelper from './softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js'; import SoftwareProductComponentsNetworkActionHelper from './softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js'; +import SoftwareProductDependenciesActionHelper from './softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js'; +import OnboardActionHelper from './onboard/OnboardActionHelper.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 ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; import store from 'sdc-app/AppStore.js'; function setCurrentScreen(dispatch, screen, props = {}) { @@ -39,7 +37,8 @@ function setCurrentScreen(dispatch, screen, props = {}) { type: actionTypes.SET_CURRENT_SCREEN, currentScreen: { screen, - props + props, + forceBreadCrumbsUpdate: true } }); } @@ -48,21 +47,61 @@ function getCurrentLicenseModelVersion(licenseModelId) { return store.getState().licenseModelList.find(({id}) => id === licenseModelId).version; } +function getCurrentSoftwareProductVersion(softwareProductId) { + return store.getState().softwareProductList.find(({id}) => id === softwareProductId).version; +} + export default { navigateToOnboardingCatalog(dispatch) { LicenseModelActionHelper.fetchLicenseModels(dispatch); + LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); SoftwareProductActionHelper.fetchSoftwareProductList(dispatch); + SoftwareProductActionHelper.fetchFinalizedSoftwareProductList(dispatch); + OnboardActionHelper.resetOnboardStore(dispatch); setCurrentScreen(dispatch, enums.SCREEN.ONBOARDING_CATALOG); }, + autoSaveBeforeNavigate(dispatch, {softwareProductId, version, vspComponentId, dataToSave}) { + if(softwareProductId) { + if(vspComponentId) { + return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, { + softwareProductId, version, vspComponentId, + componentData: dataToSave.componentData, + qdata: dataToSave.qdata + }); + } + return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, { + softwareProduct: dataToSave.softwareProduct, + qdata: dataToSave.qdata + }); + } + return Promise.resolve(); + }, + + navigateToLicenseModelOverview(dispatch, {licenseModelId, version}) { + if (!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + + /** + * TODO change to specific rest + */ + + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + LicenseModelActionHelper.fetchLicenseModelItems(dispatch, {licenseModelId, version}).then(() =>{ + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_MODEL_OVERVIEW, {licenseModelId, version}); + }); + + }); + }, 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}); + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_AGREEMENTS, {licenseModelId, version}); }); }, @@ -71,7 +110,7 @@ export default { version = getCurrentLicenseModelVersion(licenseModelId); } FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); - setCurrentScreen(dispatch, enums.SCREEN.FEATURE_GROUPS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.FEATURE_GROUPS, {licenseModelId, version}); }, navigateToEntitlementPools(dispatch, {licenseModelId, version}) { @@ -79,7 +118,7 @@ export default { version = getCurrentLicenseModelVersion(licenseModelId); } EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); - setCurrentScreen(dispatch, enums.SCREEN.ENTITLEMENT_POOLS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.ENTITLEMENT_POOLS, {licenseModelId, version}); }, navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}) { @@ -87,13 +126,30 @@ export default { version = getCurrentLicenseModelVersion(licenseModelId); } LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); - setCurrentScreen(dispatch, enums.SCREEN.LICENSE_KEY_GROUPS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_KEY_GROUPS, {licenseModelId, version}); + }, + + navigateToLicenseModelActivityLog(dispatch, {licenseModelId, version}){ + if(!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: licenseModelId, versionId: version.id}); + setCurrentScreen(dispatch, enums.SCREEN.ACTIVITY_LOG, {licenseModelId, version}); }, navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version, licensingVersion}) { + + if (!version) { + version = getCurrentSoftwareProductVersion(softwareProductId); + } + + SoftwareProductComponentsActionHelper.clearComponentsStore(dispatch); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}).then(response => { if(!licensingVersion) { licensingVersion = response[0].licensingVersion; + if (!licensingVersion) { + licensingVersion = {id: '1.0', label: '1.0'}; + } } if (!licenseModelId) { licenseModelId = response[0].vendorId; @@ -101,74 +157,84 @@ export default { SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}); SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); - + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(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}); + navigateToSoftwareProductDetails(dispatch, {softwareProductId, version}) { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DETAILS, {softwareProductId, version}); }, - navigateToSoftwareProductAttachments(dispatch, {softwareProductId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS, {softwareProductId}); + navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}) { + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS, {softwareProductId, version}); }, navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}) { if (softwareProductId) { SoftwareProductProcessesActionHelper.fetchProcessesList(dispatch, {softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES, {softwareProductId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES, {softwareProductId, version}); }, navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version}) { if (softwareProductId) { SoftwareProductNetworksActionHelper.fetchNetworksList(dispatch, {softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS, {softwareProductId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS, {softwareProductId, version}); + }, + + navigateToSoftwareProductDependencies(dispatch, {softwareProductId, version}) { + SoftwareProductDependenciesActionHelper.fetchDependencies(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES, {softwareProductId, version}); + }, + + navigateToSoftwareProductComponents(dispatch, {softwareProductId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId, version}); }, - navigateToSoftwareProductComponents(dispatch, {softwareProductId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId}); + navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version}){ + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: softwareProductId, versionId: version.id}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG, {softwareProductId, version}); }, navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId, version}) { if (componentId && softwareProductId) { SoftwareProductComponentProcessesActionHelper.fetchProcessesList(dispatch, {componentId, softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, {softwareProductId, componentId, version}); }, - navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}){ - if (componentId && softwareProductId) { - SoftwareProductComponentsMonitoringAction.fetchExistingFiles(dispatch, {componentId, softwareProductId}); + navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, version, componentId}){ + if (componentId && softwareProductId && version) { + SoftwareProductComponentsMonitoringAction.fetchExistingFiles(dispatch, {componentId, softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING, {softwareProductId, componentId, version}); }, - navigateToComponentStorage(dispatch, {softwareProductId, componentId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE, {softwareProductId, componentId}); + navigateToComponentStorage(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE, {softwareProductId, version, componentId}); }, - navigateToComponentCompute(dispatch, {softwareProductId, componentId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, componentId}); + navigateToComponentCompute(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, version, componentId}); }, navigateToComponentNetwork(dispatch, {softwareProductId, componentId, version}) { SoftwareProductComponentsNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK, {softwareProductId, version, 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}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL, {softwareProductId, version, componentId}); }, navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version}) { @@ -182,7 +248,9 @@ export default { }); }, - navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, componentId}); + navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, version, componentId}); } + }; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js deleted file mode 100644 index 4772c8d9af..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * ============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 deleted file mode 100644 index f2a9db1342..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx +++ /dev/null @@ -1,147 +0,0 @@ -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 index d9c177f606..7811950073 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -29,16 +24,21 @@ export const enums = keyMirror({ BREADCRUMS: { LICENSE_MODEL: 'LICENSE_MODEL', + LICENSE_MODEL_OVERVIEW: 'LICENSE_MODEL_OVERVIEW', LICENSE_AGREEMENTS: 'LICENSE_AGREEMENTS', FEATURE_GROUPS: 'FEATURE_GROUPS', ENTITLEMENT_POOLS: 'ENTITLEMENT_POOLS', LICENSE_KEY_GROUPS: 'LICENSE_KEY_GROUPS', + ACTIVITY_LOG: 'ACTIVITY_LOG', SOFTWARE_PRODUCT: 'SOFTWARE_PRODUCT', + SOFTWARE_PRODUCT_LANDING_PAGE: 'SOFTWARE_PRODUCT_LANDING_PAGE', 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_DEPENDENCIES: 'SOFTWARE_PRODUCT_DEPENDENCIES', + SOFTWARE_PRODUCT_ACTIVITY_LOG: 'SOFTWARE_PRODUCT_ACTIVITY_LOG', SOFTWARE_PRODUCT_COMPONENTS: 'SOFTWARE_PRODUCT_COMPONENTS', SOFTWARE_PRODUCT_COMPONENT_PROCESSES: 'SOFTWARE_PRODUCT_COMPONENT_PROCESSES', SOFTWARE_PRODUCT_COMPONENT_STORAGE: 'SOFTWARE_PRODUCT_COMPONENT_STORAGE', @@ -50,16 +50,20 @@ export const enums = keyMirror({ SCREEN: { ONBOARDING_CATALOG: null, + LICENSE_MODEL_OVERVIEW: null, LICENSE_AGREEMENTS: null, FEATURE_GROUPS: null, ENTITLEMENT_POOLS: null, LICENSE_KEY_GROUPS: null, + ACTIVITY_LOG: null, SOFTWARE_PRODUCT_LANDING_PAGE: null, SOFTWARE_PRODUCT_DETAILS: null, SOFTWARE_PRODUCT_ATTACHMENTS: null, SOFTWARE_PRODUCT_PROCESSES: null, SOFTWARE_PRODUCT_NETWORKS: null, + SOFTWARE_PRODUCT_DEPENDENCIES: null, + SOFTWARE_PRODUCT_ACTIVITY_LOG: null, SOFTWARE_PRODUCT_COMPONENTS: null, SOFTWARE_PRODUCT_COMPONENT_PROCESSES: null, SOFTWARE_PRODUCT_COMPONENT_COMPUTE: null, diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx index c4627b11b3..e8a844b03f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import ReactDOM from 'react-dom'; import {connect} from 'react-redux'; @@ -9,8 +24,12 @@ 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 Onboard from './onboard/Onboard.js'; import LicenseModel from './licenseModel/LicenseModel.js'; +import LicenseModelOverview from './licenseModel/overview/LicenseModelOverview.js'; +import ActivityLog from 'nfvo-components/activity-log/ActivityLog.js'; +import {doesHeatDataExist} from './softwareProduct/attachments/SoftwareProductAttachmentsUtils.js'; + import LicenseAgreementListEditor from './licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; import FeatureGroupListEditor from './licenseModel/featureGroups/FeatureGroupListEditor.js'; import LicenseKeyGroupsListEditor from './licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js'; @@ -21,6 +40,7 @@ import SoftwareProductDetails from './softwareProduct/details/SoftwareProductDe import SoftwareProductAttachments from './softwareProduct/attachments/SoftwareProductAttachments.js'; import SoftwareProductProcesses from './softwareProduct/processes/SoftwareProductProcesses.js'; import SoftwareProductNetworks from './softwareProduct/networks/SoftwareProductNetworks.js'; +import SoftwareProductDependencies from './softwareProduct/dependencies/SoftwareProductDependencies.js'; import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponentsList.js'; import SoftwareProductComponentProcessesList from './softwareProduct/components/processes/SoftwareProductComponentProcessesList.js'; import SoftwareProductComponentStorage from './softwareProduct/components/storage/SoftwareProductComponentStorage.js'; @@ -30,8 +50,12 @@ import SoftwareProductComponentsCompute from './softwareProduct/components/compu 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 {statusEnum as VCItemStatus} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import HeatSetupActionHelper from './softwareProduct/attachments/setup/HeatSetupActionHelper.js'; -import {enums} from './OnboardingConstants.js'; +import {actionTypes, enums} from './OnboardingConstants.js'; import OnboardingActionHelper from './OnboardingActionHelper.js'; @@ -64,17 +88,21 @@ class OnboardingView extends React.Component { {(() => { switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: - return <OnboardingCatalog {...props}/>; + return <Onboard {...props}/>; case enums.SCREEN.LICENSE_AGREEMENTS: case enums.SCREEN.FEATURE_GROUPS: case enums.SCREEN.ENTITLEMENT_POOLS: case enums.SCREEN.LICENSE_KEY_GROUPS: + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + case enums.SCREEN.ACTIVITY_LOG: return ( <LicenseModel currentScreen={currentScreen}> { (()=>{ switch(screen) { + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + return <LicenseModelOverview {...props}/>; case enums.SCREEN.LICENSE_AGREEMENTS: return <LicenseAgreementListEditor {...props}/>; case enums.SCREEN.FEATURE_GROUPS: @@ -83,6 +111,8 @@ class OnboardingView extends React.Component { return <EntitlementPoolsListEditor {...props}/>; case enums.SCREEN.LICENSE_KEY_GROUPS: return <LicenseKeyGroupsListEditor {...props}/>; + case enums.SCREEN.ACTIVITY_LOG: + return <ActivityLog {...props}/>; } })() } @@ -94,6 +124,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: @@ -102,6 +133,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: return ( <SoftwareProduct currentScreen={currentScreen}> { @@ -112,11 +144,13 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: return <SoftwareProductDetails {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: - return <SoftwareProductAttachments className='no-padding-content-area' {...props} />; + 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_DEPENDENCIES: + return <SoftwareProductDependencies {...props} />; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: return <SoftwareProductComponentsList {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: @@ -133,6 +167,8 @@ class OnboardingView extends React.Component { return <SoftwareProductComponentLoadBalancing{...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: return <SoftwareProductComponentsMonitoring {...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: + return <ActivityLog {...props}/>; } })() } @@ -184,151 +220,195 @@ export default class OnboardingPunchOut { 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(); - + let {currentScreen, softwareProductList, softwareProduct: {softwareProductEditor: {data: vspData = {}}, + softwareProductComponents = {}, softwareProductQuestionnaire = {}}, + licenseModelList, licenseModel: {licenseModelEditor: {data: {id: currentLicenseModelId, version: currentLicenseModelVersion} = {}}}} = store.getState(); + let {id: currentSoftwareProductId, version: currentSoftwareProductVersion} = vspData; + let {componentEditor: {data: componentData = {}, qdata: componentQData = {}}} = softwareProductComponents; 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); + const [, screenType, prevVspId, , prevComponentId] = this.prevSelectedKeys || []; + let preNavigate = Promise.resolve(); + if(screenType === enums.BREADCRUMS.SOFTWARE_PRODUCT && vspData.status === VCItemStatus.CHECK_OUT_STATUS && VersionControllerUtils.isCheckedOutByCurrentUser(vspData)) { + let dataToSave = prevVspId ? prevComponentId ? {componentData, qdata: componentQData} : {softwareProduct: vspData, qdata: softwareProductQuestionnaire.qdata} : {}; + preNavigate = OnboardingActionHelper.autoSaveBeforeNavigate(dispatch, { + softwareProductId: prevVspId, + version: currentSoftwareProductVersion, + vspComponentId: prevComponentId, + dataToSave + }); } + + let {currentScreen: {props: {softwareProductId}}, softwareProduct: {softwareProductAttachments: {heatSetup, heatSetupCache}}} = store.getState(); + let heatSetupPopupPromise = currentScreen.screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS ? + HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : + Promise.resolve(); + Promise.all([preNavigate, heatSetupPopupPromise]).then(() => { + 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_MODEL_OVERVIEW; + } + if (currentLicenseModelId !== licenseModelId) { + currentLicenseModelVersion = licenseModelList.find(lm => lm.id === licenseModelId).version; + } + switch (licenseModelScreen) { + case enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW: + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + 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; + case enums.BREADCRUMS.ACTIVITY_LOG: + OnboardingActionHelper.navigateToLicenseModelActivityLog(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_LANDING_PAGE: + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + 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_DEPENDENCIES: + OnboardingActionHelper.navigateToSoftwareProductDependencies(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ACTIVITY_LOG: + OnboardingActionHelper.navigateToSoftwareProductActivityLog(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); + } + }).catch(() => { + store.dispatch({ + type: actionTypes.SET_CURRENT_SCREEN, + currentScreen: { + ...currentScreen, + forceBreadCrumbsUpdate: true + } + }); + }); } } handleStoreChange() { - let {currentScreen, licenseModelList, softwareProductList} = store.getState(); - let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList}; - - if (!isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { + let {currentScreen, licenseModelList, softwareProductList, + softwareProduct: {softwareProductComponents: {componentsList}, softwareProductAttachments: {heatSetup}}} = store.getState(); + let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup}; + if (currentScreen.forceBreadCrumbsUpdate || !isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { this.prevBreadcrumbsData = breadcrumbsData; this.breadcrumbsPrefixSelected = false; this.programmaticBreadcrumbsUpdate = true; - let breadcrumbs = this.buildBreadcrumbs(breadcrumbsData); this.onEvent('breadcrumbsupdated', breadcrumbs); + store.dispatch({ + type: actionTypes.SET_CURRENT_SCREEN, + currentScreen: { + ...currentScreen, + forceBreadCrumbsUpdate: false + } + }); } } - buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList}) { - let componentsList; - if(props.componentId) { - componentsList = store.getState().softwareProduct.softwareProductComponents.componentsList; - } + buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList, componentsList, heatSetup}) { let screenToBreadcrumb; switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: @@ -338,11 +418,15 @@ export default class OnboardingPunchOut { case enums.SCREEN.FEATURE_GROUPS: case enums.SCREEN.ENTITLEMENT_POOLS: case enums.SCREEN.LICENSE_KEY_GROUPS: + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + case enums.SCREEN.ACTIVITY_LOG: 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 + [enums.SCREEN.LICENSE_KEY_GROUPS]: enums.BREADCRUMS.LICENSE_KEY_GROUPS, + [enums.SCREEN.LICENSE_MODEL_OVERVIEW]: enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW, + [enums.SCREEN.ACTIVITY_LOG]: enums.BREADCRUMS.ACTIVITY_LOG }; return [ { @@ -365,6 +449,9 @@ export default class OnboardingPunchOut { }, { selectedKey: screenToBreadcrumb[screen], menuItems: [{ + key: enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW, + displayText: i18n('Overview') + },{ key: enums.BREADCRUMS.LICENSE_AGREEMENTS, displayText: i18n('License Agreements') }, { @@ -376,6 +463,9 @@ export default class OnboardingPunchOut { }, { key: enums.BREADCRUMS.LICENSE_KEY_GROUPS, displayText: i18n('License Key Groups') + }, { + key: enums.BREADCRUMS.ACTIVITY_LOG, + displayText: i18n('Activity Log') }] } ]; @@ -385,6 +475,8 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: @@ -395,11 +487,14 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: screenToBreadcrumb = { + [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_LANDING_PAGE, [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 + [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG]: enums.BREADCRUMS.SOFTWARE_PRODUCT_ACTIVITY_LOG }; let componentScreenToBreadcrumb = { [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, @@ -438,24 +533,46 @@ export default class OnboardingPunchOut { displayText: name })) }, - ...(screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] : [{ + ...(/*screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] :*/ [{ selectedKey: screenToBreadcrumb[screen] || enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, menuItems: [{ + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_LANDING_PAGE, + displayText: i18n('Overview') + }, { 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_DEPENDENCIES, + displayText: i18n('Components Dependencies') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, + displayText: i18n('Attachments') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ACTIVITY_LOG, + displayText: i18n('Activity Log') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, displayText: i18n('Components') - }] + }].filter(item => { + let isHeatData = doesHeatDataExist(heatSetup); + let isComponentsData = componentsList.length > 0; + switch (item.key) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: + return isHeatData; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: + return isComponentsData; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES: + return isComponentsData; + default: + return true; + } + }) }]) ]; if(props.componentId) { diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js index 9af2427243..46fc58db95 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js @@ -1,26 +1,21 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes, enums} from './OnboardingConstants.js'; -export const currentScreenReducer = (state = {screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => { +export const currentScreenReducer = (state = {forceBreadCrumbsUpdate: false, screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => { if (action.type === actionTypes.SET_CURRENT_SCREEN) { return action.currentScreen; } diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js index 92d53a3d4f..9428dd5829 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js @@ -1,36 +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 - * + * + * 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========================================================= + * 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. */ - 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'; - +import finalizedSoftwareProductReducer from './softwareProduct/FinalizedSoftwareProductReducer.js'; +import onboardReducer from './onboard/OnboardReducer.js'; export default { currentScreen: currentScreenReducer, licenseModelList: licenseModelListReducer, finalizedLicenseModelList: finalizedLicenseModelListReducer, + finalizedSoftwareProductList: finalizedSoftwareProductReducer, licenseModel: licenseModelReducer, softwareProduct: softwareProductReducer, - softwareProductList: softwareProductListReducer + softwareProductList: softwareProductListReducer, + onboard: onboardReducer }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js index a851e77dc8..cc9d9c536d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './LicenseModelConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js index ad91a0da65..e21b0a81b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js @@ -1,30 +1,24 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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 ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; @@ -45,6 +39,11 @@ const buildNavigationBarProps = (licenseModel, screen) => { name: vendorName, items: [ { + id: navigationItems.LICENSE_MODEL_OVERVIEW, + name: i18n('Overview'), + meta + }, + { id: navigationItems.LICENSE_AGREEMENTS, name: i18n('License Agreements'), meta @@ -63,15 +62,22 @@ const buildNavigationBarProps = (licenseModel, screen) => { id: navigationItems.LICENSE_KEY_GROUPS, name: i18n('License Key Groups'), meta + }, + { + id: navigationItems.ACTIVITY_LOG, + name: i18n('Activity Log'), + meta } ] }]; const activeItemId = ({ + [enums.SCREEN.LICENSE_MODEL_OVERVIEW]: navigationItems.LICENSE_MODEL_OVERVIEW, [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 + [enums.SCREEN.LICENSE_KEY_GROUPS]: navigationItems.LICENSE_KEY_GROUPS, + [enums.SCREEN.ACTIVITY_LOG]: navigationItems.ACTIVITY_LOG })[screen]; return { @@ -82,9 +88,7 @@ const buildNavigationBarProps = (licenseModel, screen) => { 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}; + let {status, isCheckedOut} = VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser); return { version, @@ -104,29 +108,46 @@ const mapStateToProps = ({licenseModel: {licenseModelEditor}}, {currentScreen: { const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {licenseModelId}}}) => { + return { - onVersionControllerAction: action => - LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => { + onVersionControllerAction: (action, version) => + LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action, version}).then((newVersion) => { switch(screen) { + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + /** + * TODO change to specific rest + */ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: newVersion}); + break; case enums.SCREEN.LICENSE_AGREEMENTS: - LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId}); + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: newVersion}); break; case enums.SCREEN.FEATURE_GROUPS: - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}); + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: newVersion}); break; case enums.SCREEN.ENTITLEMENT_POOLS: - EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId}); + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version: newVersion}); break; case enums.SCREEN.LICENSE_KEY_GROUPS: - LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version: newVersion}); + break; + case enums.SCREEN.ACTIVITY_LOG: + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: licenseModelId, versionId: newVersion.id}); break; } }), - onVersionSwitching: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}), - onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch), + onVersionSwitching: version => { + LicenseModelActionHelper.switchVersion(dispatch, {licenseModelId, version}); + if(screen === enums.SCREEN.ACTIVITY_LOG) { + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: licenseModelId, versionId: version.id}); + } + }, onNavigate: ({id, meta: {version}}) => { switch(id) { + case navigationItems.LICENSE_MODEL_OVERVIEW: + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version}); + break; case navigationItems.LICENSE_AGREEMENTS: OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version}); break; @@ -139,6 +160,9 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {licenseMod case navigationItems.LICENSE_KEY_GROUPS: OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}); break; + case navigationItems.ACTIVITY_LOG: + OnboardingActionHelper.navigateToLicenseModelActivityLog(dispatch, {licenseModelId, version}); + break; } } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js index a379a2c40f..186f1cbc7b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js @@ -1,29 +1,28 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes} from './LicenseModelConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.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'; +import LicenseAgreementActionHelper from './licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupsActionHelper from './featureGroups/FeatureGroupsActionHelper.js'; +import EntitlementPoolsActionHelper from './entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseKeyGroupsActionHelper from './licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -39,12 +38,32 @@ function fetchFinalizedLicenseModels() { } function fetchLicenseModelById(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl()}${licenseModelId}${versionQuery}`); + const {id: versionId} = version; + return RestAPIUtil.fetch(`${baseUrl()}${licenseModelId}/versions/${versionId}`); +} + +function putLicenseModelAction(id, action, version) { + const {id: versionId} = version; + return RestAPIUtil.put(`${baseUrl()}${id}/versions/${versionId}/actions`, {action: action}); +} + +function putLicenseModel(licenseModel) { + let {id, vendorName, description, iconRef, version: {id: versionId}} = licenseModel; + return RestAPIUtil.put(`${baseUrl()}${id}/versions/${versionId}`, { + vendorName, + description, + iconRef + }); +} + +function adjustMinorVersion(version, value) { + let ar = version.split('.'); + return ar[0] + '.' + (parseInt(ar[1]) + value); } -function putLicenseModelAction(id, action) { - return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +function adjustMajorVersion(version, value) { + let ar = version.split('.'); + return (parseInt(ar[0]) + value) + '.0'; } const LicenseModelActionHelper = { @@ -67,13 +86,11 @@ const LicenseModelActionHelper = { }, fetchLicenseModelById(dispatch, {licenseModelId, version}) { - return fetchLicenseModelById(licenseModelId, version).then(response => { - if(version) { - response.version = version; - } + + return fetchLicenseModelById(licenseModelId, version).then(response => { dispatch({ type: actionTypes.LICENSE_MODEL_LOADED, - response + response: {...response, version} }); }); }, @@ -85,17 +102,68 @@ const LicenseModelActionHelper = { }); }, - performVCAction(dispatch, {licenseModelId, action}) { - return putLicenseModelAction(licenseModelId, action).then(() => { + fetchLicenseModelItems(dispatch, {licenseModelId, version}) { + return Promise.all([ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version}), + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}), + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}), + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}) + ]); + }, + + performVCAction(dispatch, {licenseModelId, action, version}) { + return putLicenseModelAction(licenseModelId, action, version).then(() => { if(action === vcActionsEnum.SUBMIT){ dispatch({ - type: NotificationConstants.NOTIFY_SUCCESS, - data: {title: i18n('Submit Succeeded'), msg: i18n('This license model successfully submitted'), timeout: 2000} + type: modalActionTypes.GLOBAL_MODAL_SUCCESS, + data: { + title: i18n('Submit Succeeded'), + msg: i18n('This license model successfully submitted'), + cancelButtonText: i18n('OK'), + timeout: 2000 + } }); } - return LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId}); + + let newVersionId = version.id; + /* + TODO Temorary switch to change version label + */ + switch(action) { + case vcActionsEnum.CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, 1); + break; + case vcActionsEnum.UNDO_CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, -1); + break; + case vcActionsEnum.SUBMIT: + newVersionId = adjustMajorVersion(version.label, 1); + } + + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version:{id: newVersionId, label: newVersionId}}); + return Promise.resolve({id: newVersionId, label: newVersionId}); + }); + }, + + switchVersion(dispatch, {licenseModelId, version}) { + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version: {id: version.id, label: version.label}}).then(() => { + LicenseModelActionHelper.fetchLicenseModelItems(dispatch, {licenseModelId, version}); + }); + }, + + saveLicenseModel(dispatch, {licenseModel}) { + return putLicenseModel(licenseModel).then(() => { + dispatch({ + type: actionTypes.ADD_LICENSE_MODEL, + licenseModel + }); + dispatch({ + type: actionTypes.LICENSE_MODEL_LOADED, + response: licenseModel + }); }); } + }; 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 index 13fa9f5284..4ba10c3a68 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -29,8 +24,10 @@ export const actionTypes = keyMirror({ }); export const navigationItems = keyMirror({ - LICENSE_AGREEMENTS: 'License Agreements', - FEATURE_GROUPS: 'Feature Groups', - ENTITLEMENT_POOLS: 'Entitlement Pools', - LICENSE_KEY_GROUPS: 'License Key Groups' + LICENSE_MODEL_OVERVIEW: 'overview', + LICENSE_AGREEMENTS: 'license-agreements', + FEATURE_GROUPS: 'feature-groups', + ENTITLEMENT_POOLS: 'entitlement-pools', + LICENSE_KEY_GROUPS: 'license-key-groups', + ACTIVITY_LOG: 'activity-log' }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js index e92e32aa9e..add5ac6a98 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js @@ -1,28 +1,23 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './LicenseModelConstants.js'; export default (state = {}, action) => { switch (action.type) { - case actionTypes.LICENSE_MODEL_LOADED: + case actionTypes.LICENSE_MODEL_LOADED: return { ...state, data: action.response diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js index 8874c4ce21..fd73b462a3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './LicenseModelConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js index 5982b9f8ab..9a2d114bdc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js @@ -1,66 +1,85 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {combineReducers} from 'redux'; +import activityLogReducer from 'nfvo-components/activity-log/ActivityLogReducer.js'; + 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'; + +import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; + +import {actionTypes as licenseModelOverviewConstants, selectedButton, VLM_DESCRIPTION_FORM} from './overview/LicenseModelOverviewConstants.js'; export default combineReducers({ - licenseModelCreation: licenseModelCreationReducer, + licenseModelCreation: createPlainDataReducer(licenseModelCreationReducer), licenseModelEditor: licenseModelEditorReducer, licenseAgreement: combineReducers({ - licenseAgreementEditor: licenseAgreementEditorReducer, - licenseAgreementList: licenseAgreementListReducer, - licenseAgreementToDelete: (state = false, action) => action.type === licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM ? action.licenseAgreementToDelete : state + licenseAgreementEditor: createPlainDataReducer(licenseAgreementEditorReducer), + licenseAgreementList: licenseAgreementListReducer }), featureGroup: combineReducers({ - featureGroupEditor: featureGroupsEditorReducer, - featureGroupsList: featureGroupsListReducer, - featureGroupToDelete: (state = false, action) => action.type === featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM ? action.featureGroupToDelete : state + featureGroupEditor: createPlainDataReducer(featureGroupsEditorReducer), + featureGroupsList: featureGroupsListReducer }), entitlementPool: combineReducers({ - entitlementPoolEditor: entitlementPoolsEditorReducer, - entitlementPoolsList: entitlementPoolsListReducer, - entitlementPoolToDelete: (state = false, action) => action.type === entitlementPoolsConstants.ENTITLEMENT_POOLS_DELETE_CONFIRM ? action.entitlementPoolToDelete : state + entitlementPoolEditor: createPlainDataReducer(entitlementPoolsEditorReducer), + entitlementPoolsList: entitlementPoolsListReducer }), licenseKeyGroup: combineReducers({ - licenseKeyGroupsEditor: licenseKeyGroupsEditorReducer, - licenseKeyGroupsList: licenseKeyGroupsListReducer, - licenseKeyGroupToDelete: (state = false, action) => action.type === licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM ? action.licenseKeyGroupToDelete : state + licenseKeyGroupsEditor: createPlainDataReducer(licenseKeyGroupsEditorReducer), + licenseKeyGroupsList: licenseKeyGroupsListReducer }), + licenseModelOverview: combineReducers({ + selectedTab: (state = selectedButton.VLM_LIST_VIEW, action) => action.type === licenseModelOverviewConstants.LICENSE_MODEL_OVERVIEW_TAB_SELECTED ? action.buttonTab : state, + descriptionEditor: createPlainDataReducer(function(state = false, action) { + if (action.type === licenseModelOverviewConstants.LM_DATA_CHANGED) { + return { + ...state, + data : { + description : action.description + }, + formReady: null, + formName: VLM_DESCRIPTION_FORM, + genericFieldInfo: { + 'description': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + } + } + }; + //return action.description; + } else { + return state; + } + } + )}), + activityLog: activityLogReducer }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js index 63d0f27b6a..d85618c85f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js @@ -1,40 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; import LicenseModelCreationActionHelper from './LicenseModelCreationActionHelper.js'; import LicenseModelCreationView from './LicenseModelCreationView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; + +export const mapStateToProps = ({licenseModelList, licenseModel: {licenseModelCreation}}) => { + let {genericFieldInfo} = licenseModelCreation; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); -const mapStateToProps = ({licenseModel: {licenseModelCreation}}) => licenseModelCreation; + let VLMNames = {}; + for (let i = 0; i < licenseModelList.length; i++) { + VLMNames[licenseModelList[i].vendorName] = licenseModelList[i].id; + } + + return {...licenseModelCreation, isFormValid: isFormValid, VLMNames}; +}; -const mapActionsToProps = (dispatch) => { +export const mapActionsToProps = (dispatch) => { return { - onDataChanged: deltaData => LicenseModelCreationActionHelper.dataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => LicenseModelCreationActionHelper.close(dispatch), onSubmit: (licenseModel) => { LicenseModelCreationActionHelper.close(dispatch); - LicenseModelCreationActionHelper.createLicenseModel(dispatch, {licenseModel}).then(licenseModelId => { - OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId}); + LicenseModelCreationActionHelper.createLicenseModel(dispatch, {licenseModel}).then(response => { + LicenseModelActionHelper.fetchLicenseModels(dispatch).then(() => { + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId: response.value}); + }); }); - } + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js index c2a0409bd2..51f5b7f276 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js @@ -1,27 +1,24 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -29,7 +26,7 @@ function baseUrl() { } function createLicenseModel(licenseModel) { - return RestAPIUtil.create(baseUrl(), { + return RestAPIUtil.post(baseUrl(), { vendorName: licenseModel.vendorName, description: licenseModel.description, iconRef: 'icon' @@ -43,30 +40,28 @@ export default { dispatch({ type: actionTypes.OPEN }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.LICENSE_MODEL_CREATION, + title: i18n('New License Model') + } + }); }, close(dispatch){ dispatch({ type: actionTypes.CLOSE }); - }, - dataChanged(dispatch, {deltaData}){ dispatch({ - type: actionTypes.DATA_CHANGED, - deltaData + type: modalActionTypes.GLOBAL_MODAL_CLOSE }); }, createLicenseModel(dispatch, {licenseModel}){ - return createLicenseModel(licenseModel).then(response => { - LicenseModelActionHelper.addLicenseModel(dispatch, { - licenseModel: { - ...licenseModel, - id: response.value - } - }); - return response.value; - }); + return createLicenseModel(licenseModel); } + }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js index 603d177048..28f1175676 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js @@ -1,27 +1,23 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ OPEN: null, - CLOSE: null, - DATA_CHANGED: null + CLOSE: null }); + +export const LICENSE_MODEL_CREATION_FORM_NAME = 'LMCREATIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js index a54d1b3089..879d356de2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js @@ -1,42 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from './LicenseModelCreationConstants.js'; +import {actionTypes, LICENSE_MODEL_CREATION_FORM_NAME} from './LicenseModelCreationConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.OPEN: return { ...state, - data: {} + formReady: null, + formName: LICENSE_MODEL_CREATION_FORM_NAME, + data: {}, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + }, + 'vendorName' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}] + } + } }; 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 index 4dccc9e1c4..80040460c0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx @@ -1,7 +1,24 @@ +/*! + * 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. + */ 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 Validator from 'nfvo-utils/Validator.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import {LICENSE_MODEL_CREATION_FORM_NAME} from './LicenseModelCreationConstants.js'; const LicenseModelPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -13,39 +30,49 @@ class LicenseModelCreationView extends React.Component { static propTypes = { data: LicenseModelPropType, + VLMNames: React.PropTypes.object, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, + onValidateForm: React.PropTypes.func.isRequired, onCancel: React.PropTypes.func.isRequired }; render() { - let {data = {}, onDataChanged} = this.props; + let {data = {}, onDataChanged, genericFieldInfo} = this.props; let {vendorName, description} = data; return ( <div> - <ValidationForm + {genericFieldInfo && <Form ref='validationForm' hasButtons={true} onSubmit={ () => this.submit() } onReset={ () => this.props.onCancel() } - labledButtons={true}> - <ValidationInput + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.validate() }> + <Input value={vendorName} label={i18n('Vendor Name')} - ref='vendor-name' - onChange={vendorName => onDataChanged({vendorName})} - validations={{maxLength: 25, required: true}} + data-test-id='vendor-name' + onChange={vendorName => onDataChanged({vendorName}, LICENSE_MODEL_CREATION_FORM_NAME, {vendorName: name => this.validateName(name)})} + isValid={genericFieldInfo.vendorName.isValid} + errorText={genericFieldInfo.vendorName.errorText} type='text' + isRequired={true} className='field-section'/> - <ValidationInput + <Input + isRequired={true} value={description} label={i18n('Description')} - ref='description' - onChange={description => onDataChanged({description})} - validations={{maxLength: 1000, required: true}} + data-test-id='vendor-description' + overlayPos='bottom' + onChange={description => onDataChanged({description}, LICENSE_MODEL_CREATION_FORM_NAME)} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} type='textarea' className='field-section'/> - </ValidationForm> + </Form>} </div> ); } @@ -55,6 +82,18 @@ class LicenseModelCreationView extends React.Component { const {data:licenseModel} = this.props; this.props.onSubmit(licenseModel); } + + validateName(value) { + const {data: {id}, VLMNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: VLMNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('License model by the name \'' + value + '\' already exists. License model name must be unique')}; + } + + validate() { + this.props.onValidateForm(LICENSE_MODEL_CREATION_FORM_NAME); + } } 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 index 631597a5b0..fe95b034dd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js @@ -1,40 +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 - * + * + * 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========================================================= + * 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. */ - 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) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/entitlement-pools`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/entitlement-pools`; } -function fetchEntitlementPoolsList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +function fetchEntitlementPoolsList(licenseModelId, version) { + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function postEntitlementPool(licenseModelId, entitlementPool) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function postEntitlementPool(licenseModelId, entitlementPool, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, @@ -49,8 +44,8 @@ function postEntitlementPool(licenseModelId, entitlementPool) { } -function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool) { - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${entitlementPool.id}`, { +function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool, version) { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${entitlementPool.id}`, { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, @@ -64,8 +59,8 @@ function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlement }); } -function deleteEntitlementPool(licenseModelId, entitlementPoolId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${entitlementPoolId}`); +function deleteEntitlementPool(licenseModelId, entitlementPoolId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}`); } @@ -84,8 +79,8 @@ export default { }); }, - deleteEntitlementPool(dispatch, {licenseModelId, entitlementPoolId}) { - return deleteEntitlementPool(licenseModelId, entitlementPoolId).then(() => { + deleteEntitlementPool(dispatch, {licenseModelId, entitlementPoolId, version}) { + return deleteEntitlementPool(licenseModelId, entitlementPoolId, version).then(() => { dispatch({ type: entitlementPoolsActionTypes.DELETE_ENTITLEMENT_POOL, entitlementPoolId @@ -106,9 +101,9 @@ export default { }); }, - saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}) { + saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool, version}) { if (previousEntitlementPool) { - return putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool).then(() => { + return putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool, version).then(() => { dispatch({ type: entitlementPoolsActionTypes.EDIT_ENTITLEMENT_POOL, entitlementPool @@ -116,11 +111,12 @@ export default { }); } else { - return postEntitlementPool(licenseModelId, entitlementPool).then(response => { + return postEntitlementPool(licenseModelId, entitlementPool, version).then(response => { dispatch({ type: entitlementPoolsActionTypes.ADD_ENTITLEMENT_POOL, entitlementPool: { ...entitlementPool, + referencingFeatureGroups: [], id: response.value } }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx deleted file mode 100644 index 04f038f5f0..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx +++ /dev/null @@ -1,51 +0,0 @@ -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 index 8a855076f3..ba0b238b17 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js @@ -1,25 +1,21 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; export const actionTypes = keyMirror({ @@ -27,7 +23,6 @@ export const actionTypes = keyMirror({ ADD_ENTITLEMENT_POOL: null, EDIT_ENTITLEMENT_POOL: null, DELETE_ENTITLEMENT_POOL: null, - ENTITLEMENT_POOLS_DELETE_CONFIRM: null, entitlementPoolsEditor: { OPEN: null, @@ -107,6 +102,14 @@ export const optionsInputValues = { ] }; +export const extractValue = (item) => { + if (item === undefined) {return '';} //TODO fix it later + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; +}; +export const extractUnits = (units) => { + if (units === undefined) {return '';} //TODO fix it later + return units === 'Absolute' ? '' : '%'; +}; - +export const SP_ENTITLEMENT_POOL_FORM = 'SPENTITLEMENTPOOL'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js index d5bd07e929..f89cf8fbb5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js @@ -1,52 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; import EntitlementPoolsEditorView from './EntitlementPoolsEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({licenseModel: {entitlementPool}}) => { - let {data} = entitlementPool.entitlementPoolEditor; - - let previousData; + let {data, genericFieldInfo, formReady} = entitlementPool.entitlementPoolEditor; + + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + let previousData, EPNames = {}; const entitlementPoolId = data ? data.id : null; if(entitlementPoolId) { previousData = entitlementPool.entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId); } + const list = entitlementPool.entitlementPoolsList; + for (let i = 0; i < list.length; i++) { + EPNames[list[i].name] = list[i].id; + } + return { data, - previousData + genericFieldInfo, + previousData, + isFormValid, + formReady, + EPNames }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { - onDataChanged: deltaData => EntitlementPoolsActionHelper.entitlementPoolsEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch), onSubmit: ({previousEntitlementPool, entitlementPool}) => { EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch); - EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}); - } + EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool, version}); + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js index 86e97ecf8d..db1a3a97ca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js @@ -1,30 +1,79 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes, defaultState} from './EntitlementPoolsConstants.js'; +import {actionTypes, defaultState, SP_ENTITLEMENT_POOL_FORM} from './EntitlementPoolsConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.entitlementPoolsEditor.OPEN: return { ...state, + formReady: null, + formName: SP_ENTITLEMENT_POOL_FORM, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + }, + 'manufacturerReferenceNumber' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 100}] + }, + 'increments' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 120}] + }, + 'operationalScope' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'thresholdUnits' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'thresholdValue' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'entitlementMetric' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'aggregationFunction' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'time' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + }, data: action.entitlementPool ? {...action.entitlementPool} : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA }; case actionTypes.entitlementPoolsEditor.DATA_CHANGED: diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx index 77c5a12e03..d484437015 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx @@ -1,14 +1,31 @@ +/*! + * 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. + */ import React from 'react'; - import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.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 Input from 'nfvo-components/input/validation/Input.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType, SP_ENTITLEMENT_POOL_FORM} 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, @@ -33,11 +50,172 @@ const EntitlementPoolPropType = React.PropTypes.shape({ }) }); +const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, thresholdValueValidation}) => { + let { + name, description, manufacturerReferenceNumber, operationalScope , aggregationFunction, thresholdUnits, thresholdValue, + increments, time, entitlementMetric} = data; + + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name}, SP_ENTITLEMENT_POOL_FORM, {name: validateName})} + isValid={genericFieldInfo.name.isValid} + isRequired={true} + errorText={genericFieldInfo.name.errorText} + label={i18n('Name')} + value={name} + data-test-id='create-ep-name' + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={true} + + isRequired={true} + onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {operationalScope: validateChoiceWithOther})} + onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], + other: operationalScope}}, SP_ENTITLEMENT_POOL_FORM, {operationalScope: validateChoiceWithOther})} + label={i18n('Operational Scope')} + data-test-id='create-ep-operational-scope' + type='select' + multiSelectedEnum={operationalScope && operationalScope.choices} + otherValue={operationalScope && operationalScope.other} + values={EntitlementPoolsOptionsInputValues.OPERATIONAL_SCOPE} + isValid={genericFieldInfo.operationalScope.isValid} + errorText={genericFieldInfo.operationalScope.errorText} /> + </GridItem> + <GridItem colSpan={2} stretch> + <Input + onChange={description => onDataChanged({description}, SP_ENTITLEMENT_POOL_FORM)} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + label={i18n('Description')} + value={description} + isRequired={true} + data-test-id='create-ep-description' + type='textarea'/> + </GridItem> + <GridItem colSpan={2}> + <div className='threshold-section'> + <Input + isRequired={true} + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({thresholdUnits: val}, SP_ENTITLEMENT_POOL_FORM); + // TODO make sure that the value is valid too + onDataChanged({thresholdValue: thresholdValue}, SP_ENTITLEMENT_POOL_FORM,{thresholdValue : thresholdValueValidation});} + + } + value={thresholdUnits} + label={i18n('Threshold Units')} + data-test-id='create-ep-threshold-units' + isValid={genericFieldInfo.thresholdUnits.isValid} + errorText={genericFieldInfo.thresholdUnits.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + {EntitlementPoolsOptionsInputValues.THRESHOLD_UNITS.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + + <Input + className='entitlement-pools-form-row-threshold-value' + onChange={thresholdValue => onDataChanged({thresholdValue}, SP_ENTITLEMENT_POOL_FORM, + {thresholdValue : thresholdValueValidation})} + label={i18n('Threshold Value')} + isValid={genericFieldInfo.thresholdValue.isValid} + errorText={genericFieldInfo.thresholdValue.errorText} + data-test-id='create-ep-threshold-value' + value={thresholdValue} + isRequired={true} + type='text'/> + </div> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: entitlementMetric, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {entitlementMetric: validateChoiceWithOther})} + onOtherChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: optionInputOther.OTHER, + other: entitlementMetric}}, SP_ENTITLEMENT_POOL_FORM, {entitlementMetric: validateChoiceWithOther})} + label={i18n('Entitlement Metric')} + data-test-id='create-ep-entitlement-metric' + type='select' + required={true} + selectedEnum={entitlementMetric && entitlementMetric.choice} + otherValue={entitlementMetric && entitlementMetric.other} + values={EntitlementPoolsOptionsInputValues.ENTITLEMENT_METRIC} + isValid={genericFieldInfo.entitlementMetric.isValid} + errorText={genericFieldInfo.entitlementMetric.errorText} /> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: aggregationFunction, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {aggregationFunction: validateChoiceWithOther})} + onOtherChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: optionInputOther.OTHER, + other: aggregationFunction}}, SP_ENTITLEMENT_POOL_FORM, {aggregationFunction: validateChoiceWithOther})} + label={i18n('Aggregate Function')} + data-test-id='create-ep-aggregate-function' + type='select' + required={true} + selectedEnum={aggregationFunction && aggregationFunction.choice} + otherValue={aggregationFunction && aggregationFunction.other} + values={EntitlementPoolsOptionsInputValues.AGGREGATE_FUNCTION} + isValid={genericFieldInfo.aggregationFunction.isValid} + errorText={genericFieldInfo.aggregationFunction.errorText} /> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber}, SP_ENTITLEMENT_POOL_FORM)} + label={i18n('Manufacturer Reference Number')} + value={manufacturerReferenceNumber} + isRequired={true} + data-test-id='create-ep-reference-number' + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={time => onDataChanged({time:{choice: time, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {time: validateChoiceWithOther})} + onOtherChange={time => onDataChanged({time:{choice: optionInputOther.OTHER, + other: time}}, SP_ENTITLEMENT_POOL_FORM, {time: validateTimeOtherValue})} + label={i18n('Time')} + data-test-id='create-ep-time' + type='select' + required={true} + selectedEnum={time && time.choice} + otherValue={time && time.other} + values={EntitlementPoolsOptionsInputValues.TIME} + isValid={genericFieldInfo.time.isValid} + errorText={genericFieldInfo.time.errorText} /> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={increments => onDataChanged({increments}, SP_ENTITLEMENT_POOL_FORM)} + label={i18n('Increments')} + value={increments} + data-test-id='create-ep-increments' + type='text'/> + </GridItem> + </GridSection> + ); +}; + class EntitlementPoolsEditorView extends React.Component { static propTypes = { data: EntitlementPoolPropType, previousData: EntitlementPoolPropType, + EPNames: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, @@ -49,119 +227,88 @@ class EntitlementPoolsEditorView extends React.Component { }; 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}; + let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo} = this.props; + 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'/> + <div> + { + genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(SP_ENTITLEMENT_POOL_FORM) } + className='entitlement-pools-form'> + <EntitlementPoolsFormContent + data={data} + genericFieldInfo={genericFieldInfo} + onDataChanged={onDataChanged} + validateName={(value)=> this.validateName(value)} + validateTimeOtherValue ={(value)=> this.validateTimeOtherValue(value)} + validateChoiceWithOther={(value)=> this.validateChoiceWithOther(value)} + thresholdValueValidation={(value, state)=> this.thresholdValueValidation(value, state)}/> + </Form> + } + </div> + ); + } - <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}/> + submit() { + const {data: entitlementPool, previousData: previousEntitlementPool} = this.props; + this.props.onSubmit({entitlementPool, previousEntitlementPool}); + } - </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'> + validateName(value) { + const {data: {id}, EPNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: EPNames}); - <ValidationInput - onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber})} - label={i18n('Manufacturer Reference Number')} - value={manufacturerReferenceNumber} - validations={{maxLength: 100, required: true}} - type='text'/> + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Entitlement pool by the name \'' + value + '\' already exists. Entitlement pool name must be unique')}; + } - <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'/> + validateTimeOtherValue(value) { + return Validator.validate('time', value.other, [{type: 'required', data: true}, {type: 'numeric', data: true}]); + } - </div> - </ValidationForm> - ); + validateChoiceWithOther(value) { + let chosen = value.choice; + // if we have an empty multiple select we have a problem since it's required + if (value.choices) { + if (value.choices.length === 0) { + return Validator.validate('field', '', [{type: 'required', data: true}]); + } else { + // continuing validation with the first chosen value in case we have the 'Other' field + chosen = value.choices[0]; + } + } + if (chosen !== optionInputOther.OTHER) { + return Validator.validate('field', chosen, [{type: 'required', data: true}]); + } else { // when 'Other' was chosen, validate other value + return Validator.validate('field', value.other, [{type: 'required', data: true}]); + } } - submit() { - const {data: entitlementPool, previousData: previousEntitlementPool} = this.props; - this.props.onSubmit({entitlementPool, previousEntitlementPool}); + thresholdValueValidation(value, state) { + + let unit = state.data.thresholdUnits; + if (unit === thresholdUnitType.PERCENTAGE) { + return Validator.validate('thresholdValue', value, [ + {type: 'required', data: true}, + {type: 'numeric', data: true}, + {type: 'maximum', data: 100}, + {type: 'minimum', data: 0}]); + } else { + return Validator.validate('thresholdValue', value, [ + {type: 'numeric', data: true}, + {type: 'required', data: true}]); + } } + } 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 index 4b21a2fea8..993ed48f2b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js @@ -1,27 +1,24 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; -import EntitlementPoolsListEditorView from './EntitlementPoolsListEditorView.jsx'; +import EntitlementPoolsListEditorView, {generateConfirmationMsg} from './EntitlementPoolsListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) => { let {entitlementPoolsList} = entitlementPool; @@ -39,13 +36,21 @@ const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onAddEntitlementPoolClick: () => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch), onEditEntitlementPoolClick: entitlementPool => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch, {entitlementPool}), - onDeleteEntitlementPool: entitlementPool => EntitlementPoolsActionHelper.openDeleteEntitlementPoolConfirm(dispatch, { - licenseModelId, - entitlementPool + onDeleteEntitlementPool: entitlementPool => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateConfirmationMsg(entitlementPool), + title: i18n('Warning'), + onConfirmed: ()=>EntitlementPoolsActionHelper.deleteEntitlementPool(dispatch, { + licenseModelId, + entitlementPoolId: entitlementPool.id, + version + }) + } }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx index 52df102503..07a6f21a1a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -6,10 +21,7 @@ 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'; - +import {extractUnits, extractValue} from './EntitlementPoolsConstants'; class EntitlementPoolsListEditorView extends React.Component { static propTypes = { @@ -33,35 +45,33 @@ class EntitlementPoolsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = 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})} + title={i18n('Entitlement Pools', {vendorName})} plusButtonTitle={i18n('Add Entitlement Pool')} onAdd={onAddEntitlementPoolClick} filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} + onFilter={value => this.setState({localFilter: value})} isReadOnlyMode={isReadOnlyMode}> {this.filterList().map(entitlementPool => this.renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode))} </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='entitlement-pools-modal'> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal 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}/> + <EntitlementPoolsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/> ) } </Modal.Body> </Modal> - - <EntitlementPoolsConfirmationModal licenseModelId={licenseModelId}/> </div> ); } @@ -92,14 +102,15 @@ class EntitlementPoolsListEditorView extends React.Component { 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='textEllipses text name'>{name}</div></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 className='entitlement-parameters'>{`${extractValue(aggregationFunction)} ${extractValue(entitlementMetric)} per ${extractValue(time)}`}</div> + <div className='entitlement-pools-count'>{`${thresholdValue ? thresholdValue : ''} ${extractUnits(thresholdUnits)}`}</div> </div> <div className='list-editor-item-view-field'> @@ -115,18 +126,22 @@ class EntitlementPoolsListEditorView extends React.Component { ); } - - - 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; + +export function generateConfirmationMsg(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> + ); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js index 63e351fce7..fefd823207 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './EntitlementPoolsConstants'; export default (state = [], action) => { switch (action.type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js index c2b269bcf9..c6249c98ca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js @@ -1,66 +1,78 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; import FeatureGroupEditorView from './FeatureGroupEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; -const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => { - +export const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => { + let {entitlementPoolsList = []} = entitlementPool; + let {licenseKeyGroupsList = []} = licenseKeyGroup; const {featureGroupEditor} = featureGroup; + let {data, selectedTab, genericFieldInfo, formReady} = featureGroupEditor; + const featureGroupId = data ? data.id : null; + const list = featureGroup.featureGroupsList; - let {data, selectedTab, selectedEntitlementPoolsButtonTab, selectedLicenseKeyGroupsButtonTab} = featureGroupEditor; + let previousData, FGNames = {}, isFormValid = true, invalidTabs = []; - let previousData; - const featureGroupId = data ? data.id : null; if (featureGroupId) { - previousData = featureGroup.featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId); + previousData = list.find(featureGroup => featureGroup.id === featureGroupId); + } + + for (let i = 0; i < list.length; i++) { + FGNames[list[i].name] = list[i].id; + } + + for (let field in genericFieldInfo) { + if (!genericFieldInfo[field].isValid) { + isFormValid = false; + let tabId = genericFieldInfo[field].tabId; + if (invalidTabs.indexOf(tabId) === -1) { + invalidTabs[invalidTabs.length] = genericFieldInfo[field].tabId; + } + } } - let {entitlementPoolsList = []} = entitlementPool; - let {licenseKeyGroupsList = []} = licenseKeyGroup; return { data, previousData, selectedTab, - selectedEntitlementPoolsButtonTab, - selectedLicenseKeyGroupsButtonTab, entitlementPoolsList, - licenseKeyGroupsList + licenseKeyGroupsList, + isFormValid, + formReady, + genericFieldInfo, + invalidTabs, + FGNames }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), 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}); - } + FeatureGroupsActionHelper.saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup, version}); + }, + onCancel: () => FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch), + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; 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 index 6fecd16b71..5ae22cba95 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx @@ -1,27 +1,125 @@ +/*! + * 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. + */ 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 Tabs from 'nfvo-components/input/validation/Tabs.jsx'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import Form from 'nfvo-components/input/validation/Form.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 Input from 'nfvo-components/input/validation/Input.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.js'; -import {state as FeatureGroupStateConstants} from './FeatureGroupsConstants.js'; +import {state as FeatureGroupStateConstants, FG_EDITOR_FORM} 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) + entitlementPoolsIds: React.PropTypes.arrayOf(React.PropTypes.string), + licenseKeyGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string) }); +const GeneralTab = ({data = {}, onDataChanged, genericFieldInfo, validateName}) => { + let {name, description, partNumber} = data; + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + groupClassName='field-section' + onChange={name => onDataChanged({name}, FG_EDITOR_FORM, {name: validateName})} + label={i18n('Name')} + data-test-id='create-fg-name' + value={name} + name='feature-group-name' + type='text' + isRequired={true} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} /> + <Input + groupClassName='field-section' + className='description-field' + onChange={description => onDataChanged({description}, FG_EDITOR_FORM)} + data-test-id='create-fg-description' + label={i18n('Description')} + value={description} + name='feature-group-description' + type='textarea' + isRequired={true} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} /> + <Input + groupClassName='field-section' + onChange={partNumber => onDataChanged({partNumber}, FG_EDITOR_FORM)} + label={i18n('Part Number')} + data-test-id='create-fg-part-number' + value={partNumber} + isRequired={true} + type='text' + isValid={genericFieldInfo.partNumber.isValid} + errorText={genericFieldInfo.partNumber.errorText} /> + </GridItem> + </GridSection> + ); +}; + +const EntitlementPoolsTab = ({entitlementPoolsList, data, onDataChanged, isReadOnlyMode}) => { + const dualBoxFilterTitle = { + left: i18n('Available Entitlement Pools'), + right: i18n('Selected Entitlement Pools') + }; + if (entitlementPoolsList.length > 0) { + return ( + <DualListboxView + isReadOnlyMode={isReadOnlyMode} + filterTitle={dualBoxFilterTitle} + selectedValuesList={data.entitlementPoolsIds} + availableList={entitlementPoolsList} + onChange={ selectedValuesList => onDataChanged( { entitlementPoolsIds: selectedValuesList }, FG_EDITOR_FORM )}/> + ); + } else { + return ( + <p>{i18n('There is no available entitlement pools')}</p> + ); + } +}; + +const LKGTab = ({licenseKeyGroupsList, data, onDataChanged, isReadOnlyMode}) => { + const dualBoxFilterTitle = { + left: i18n('Available License Key Groups'), + right: i18n('Selected License Key Groups') + }; + if (licenseKeyGroupsList.length > 0) { + return ( + <DualListboxView + isReadOnlyMode={isReadOnlyMode} + filterTitle={dualBoxFilterTitle} + selectedValuesList={data.licenseKeyGroupsIds} + availableList={licenseKeyGroupsList} + onChange={ selectedValuesList => onDataChanged( { licenseKeyGroupsIds: selectedValuesList }, FG_EDITOR_FORM )}/> + ); + } else { + return ( + <p>{i18n('There is no available licsense key groups')}</p> + ); + } +}; + class FeatureGroupEditorView extends React.Component { @@ -29,6 +127,7 @@ class FeatureGroupEditorView extends React.Component { data: FeatureGroupsPropType, previousData: FeatureGroupsPropType, isReadOnlyMode: React.PropTypes.bool, + FGNames: React.PropTypes.object, onSubmit: React.PropTypes.func, onCancel: React.PropTypes.func, @@ -36,11 +135,6 @@ class FeatureGroupEditorView extends React.Component { 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 }; @@ -49,8 +143,6 @@ class FeatureGroupEditorView extends React.Component { 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 = { @@ -60,24 +152,39 @@ class FeatureGroupEditorView extends React.Component { render() { - let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + let {selectedTab, onTabSelect, isReadOnlyMode, invalidTabs, data, onDataChanged, genericFieldInfo, entitlementPoolsList, licenseKeyGroupsList} = this.props; return ( - <ValidationForm + <div> + { genericFieldInfo && <Form ref='validationForm' hasButtons={true} onSubmit={ () => this.submit() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(FG_EDITOR_FORM) } 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> + <Tabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect} invalidTabs={invalidTabs} id='vlmFGValTabs' > + <Tab eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL} title={i18n('General')} > + <GeneralTab data={data} onDataChanged={onDataChanged} genericFieldInfo={genericFieldInfo} validateName={(value)=> this.validateName(value)}/> + </Tab> + <Tab + eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS} + title={i18n('Entitlement Pools')} > + <EntitlementPoolsTab isReadOnlyMode={isReadOnlyMode} data={data} onDataChanged={onDataChanged} entitlementPoolsList={entitlementPoolsList} /> + </Tab> + <Tab + eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENSE_KEY_GROUPS} + title={i18n('License Key Groups')} > + <LKGTab isReadOnlyMode={isReadOnlyMode} data={data} onDataChanged={onDataChanged} licenseKeyGroupsList={licenseKeyGroupsList} /> + </Tab> + </Tabs> + + </Form> } + </div> ); } @@ -86,254 +193,14 @@ class FeatureGroupEditorView extends React.Component { 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)}); - } + validateName(value) { + const {data: {id}, FGNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: FGNames}); + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Feature group by the name \'' + value + '\' already exists. Feature group name must be unique')}; } } 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 index 9ea5a31490..83473a30bb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js @@ -1,35 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; + import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; -import FeatureGroupListEditorView from './FeatureGroupListEditorView.jsx'; +import FeatureGroupListEditorView, {generateConfirmationMsg} from './FeatureGroupListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; -const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => { +export const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => { const {featureGroupEditor: {data}, featureGroupsList} = featureGroup; - let {vendorName} = licenseModelEditor.data; + let {vendorName, version} = licenseModelEditor.data; let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); - return { vendorName, + version, featureGroupsModal: { show: Boolean(data), editMode: Boolean(data && data.id) @@ -42,13 +40,19 @@ const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => 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, { + onDeleteFeatureGroupClick: (featureGroup, version) => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateConfirmationMsg(featureGroup), + title: i18n('Warning'), + onConfirmed: ()=>FeatureGroupsActionHelper.deleteFeatureGroup(dispatch, {featureGroupId: featureGroup.id, licenseModelId, version}) + } + }), + onAddFeatureGroupClick: (actualVersion) => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId, version: actualVersion}), + onEditFeatureGroupClick: (featureGroup, actualVersion) => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, { featureGroup, - licenseModelId + licenseModelId, + version: actualVersion }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx index d998f9216f..bc0f5c71c0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -6,7 +21,6 @@ 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 = { @@ -37,46 +51,45 @@ class FeatureGroupListEditorView extends React.Component { }; render() { - let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick} = this.props; + let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick, version} = this.props; const {localFilter} = this.state; - return ( <div className='feature-groups-list-editor'> <ListEditorView - title={i18n('Feature Groups for {vendorName} License Model', {vendorName})} + title={i18n('Feature Groups', {vendorName})} plusButtonTitle={i18n('Add Feature Group')} filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} - onAdd={() => onAddFeatureGroupClick()} + onFilter={value => this.setState({localFilter: value})} + onAdd={() => onAddFeatureGroupClick(version)} isReadOnlyMode={isReadOnlyMode}> - {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode))} + {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode, version))} </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}/> + {featureGroupsModal.show && <Modal show={featureGroupsModal.show} bsSize='large' animation={true} + className='onborading-modal 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 + version={version} + licenseModelId={licenseModelId} + isReadOnlyMode={isReadOnlyMode}/> + </Modal.Body> + </Modal> + } </div> ); } - renderFeatureGroupListItem(listItem, isReadOnlyMode) { + renderFeatureGroupListItem(listItem, isReadOnlyMode, version) { let {name, description, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem; return ( <ListEditorItemView key={listItem.id} - onDelete={() => this.deleteFeatureGroupItem(listItem)} - onSelect={() => this.editFeatureGroupItem(listItem)} + onDelete={() => this.deleteFeatureGroupItem(listItem, version)} + onSelect={() => this.editFeatureGroupItem(listItem, version)} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode}> <div className='list-editor-item-view-field'> @@ -120,17 +133,28 @@ class FeatureGroupListEditorView extends React.Component { } } - closeFeatureGroupsEditor() { - this.props.onCancelFeatureGroupsEditor(); - } - - editFeatureGroupItem(featureGroup) { - this.props.onEditFeatureGroupClick(featureGroup); + editFeatureGroupItem(featureGroup, version) { + this.props.onEditFeatureGroupClick(featureGroup, version); } - deleteFeatureGroupItem(featureGroup) { - this.props.onDeleteFeatureGroupClick(featureGroup); + deleteFeatureGroupItem(featureGroup, version) { + this.props.onDeleteFeatureGroupClick(featureGroup, version); } } export default FeatureGroupListEditorView; + +export function generateConfirmationMsg(featureGroupToDelete) { + let name = featureGroupToDelete ? featureGroupToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let subMsg = 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> + ); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js index 3776c01263..a2015787a6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes as featureGroupsActionConstants} from './FeatureGroupsConstants.js'; @@ -25,22 +20,22 @@ import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseMod import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; -function baseUrl(licenseModelId) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/feature-groups`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/feature-groups`; } function fetchFeatureGroupsList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function deleteFeatureGroup(licenseModelId, featureGroupId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${featureGroupId}`); +function deleteFeatureGroup(licenseModelId, featureGroupId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${featureGroupId}`); } -function addFeatureGroup(licenseModelId, featureGroup) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function addFeatureGroup(licenseModelId, featureGroup, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: featureGroup.name, description: featureGroup.description, partNumber: featureGroup.partNumber, @@ -49,13 +44,13 @@ function addFeatureGroup(licenseModelId, featureGroup) { }); } -function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup) { +function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup, version) { const {licenseKeyGroupsIds = []} = featureGroup; const {licenseKeyGroupsIds: prevLicenseKeyGroupsIds = []} = previousFeatureGroup; const {entitlementPoolsIds = []} = featureGroup; const {entitlementPoolsIds: prevEntitlementPoolsIds = []} = previousFeatureGroup; - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${featureGroup.id}`, { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${featureGroup.id}`, { name: featureGroup.name, description: featureGroup.description, partNumber: featureGroup.partNumber, @@ -75,28 +70,37 @@ export default { })); }, - deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId}) { - return deleteFeatureGroup(licenseModelId, featureGroupId).then(() => dispatch({ + deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId, version}) { + return deleteFeatureGroup(licenseModelId, featureGroupId, version).then(() => dispatch({ type: featureGroupsActionConstants.DELETE_FEATURE_GROUPS, featureGroupId })); }, - saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}) { + saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup, version}) { if (previousFeatureGroup) { - return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup).then(() => dispatch({ - type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS, - featureGroup - })); + return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup, version).then(() =>{ + dispatch({ + type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS, + featureGroup + }); + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); + }); } else { - return addFeatureGroup(licenseModelId, featureGroup).then(response => dispatch({ - type: featureGroupsActionConstants.ADD_FEATURE_GROUPS, - featureGroup: { - ...featureGroup, - id: response.value - } - })); + return addFeatureGroup(licenseModelId, featureGroup, version).then(response => { + dispatch({ + type: featureGroupsActionConstants.ADD_FEATURE_GROUPS, + featureGroup: { + ...featureGroup, + id: response.value, + referencingLicenseAgreements: [] + } + }); + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); + }); } }, @@ -107,23 +111,9 @@ export default { }); }, - 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}); + openFeatureGroupsEditor(dispatch, {featureGroup, licenseModelId, version}) { + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); dispatch({ type: featureGroupsActionConstants.featureGroupsEditor.OPEN, featureGroup @@ -136,26 +126,6 @@ export default { }); }, - 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(() => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx deleted file mode 100644 index 142ec3c4c8..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx +++ /dev/null @@ -1,48 +0,0 @@ -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 index e02c54595d..4c5a94f239 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -25,8 +20,6 @@ export const actionTypes = keyMirror({ ADD_FEATURE_GROUPS: null, EDIT_FEATURE_GROUPS: null, DELETE_FEATURE_GROUPS: null, - FEATURE_GROUPS_DELETE_CONFIRM: null, - ENTITLEMENT_POOLS_LIST_LOADED: null, @@ -34,27 +27,16 @@ export const actionTypes = keyMirror({ OPEN: null, CLOSE: null, DATA_CHANGED: null, - SELECT_TAB: null, - SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: null, - SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: null + SELECT_TAB: null } }); +export const FG_EDITOR_FORM = 'FG_EDITOR_FORM'; + 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 - }, + LICENSE_KEY_GROUPS: 3 + } }); - - - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js index 576a5358e6..001bd20d44 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js @@ -1,24 +1,20 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ -import {actionTypes} from './FeatureGroupsConstants.js'; +import {actionTypes, FG_EDITOR_FORM, state as FeatureGroupStateConstants} from './FeatureGroupsConstants.js'; @@ -27,14 +23,28 @@ export default (state = {}, action) => { case actionTypes.featureGroupsEditor.OPEN: return { ...state, - data: action.featureGroup || {} - }; - case actionTypes.featureGroupsEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData + data: action.featureGroup || {}, + formReady: null, + formName: FG_EDITOR_FORM, + genericFieldInfo: { + 'description': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + }, + 'partNumber': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + }, + 'name': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + } } }; case actionTypes.featureGroupsEditor.CLOSE: diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js index 5cf3248919..3b5f1c55e4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './FeatureGroupsConstants.js'; export default (state = [], action) => { switch (action.type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js index 9616b60b76..efc4fb758f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js @@ -1,41 +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 - * + * + * 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========================================================= + * 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. */ - 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) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-agreements`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/license-agreements`; } function fetchLicenseAgreementList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function postLicenseAgreement(licenseModelId, licenseAgreement) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function postLicenseAgreement(licenseModelId, licenseAgreement, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: licenseAgreement.name, description: licenseAgreement.description, licenseTerm: licenseAgreement.licenseTerm, @@ -44,10 +39,10 @@ function postLicenseAgreement(licenseModelId, licenseAgreement) { }); } -function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement) { +function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement, version) { const {featureGroupsIds = []} = licenseAgreement; const {featureGroupsIds: prevFeatureGroupsIds = []} = previousLicenseAgreement; - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseAgreement.id}`, { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${licenseAgreement.id}`, { name: licenseAgreement.name, description: licenseAgreement.description, licenseTerm: licenseAgreement.licenseTerm, @@ -57,8 +52,8 @@ function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAg }); } -function deleteLicenseAgreement(licenseModelId, licenseAgreementId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseAgreementId}`); +function deleteLicenseAgreement(licenseModelId, licenseAgreementId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${licenseAgreementId}`); } export default { @@ -70,21 +65,14 @@ export default { })); }, - openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}) { - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}); + openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement, version}) { + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); dispatch({ type: licenseAgreementActionTypes.licenseAgreementEditor.OPEN, licenseAgreement }); }, - licenseAgreementEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: licenseAgreementActionTypes.licenseAgreementEditor.DATA_CHANGED, - deltaData - }); - }, - closeLicenseAgreementEditor(dispatch) { dispatch({ type: licenseAgreementActionTypes.licenseAgreementEditor.CLOSE @@ -92,9 +80,9 @@ export default { }, - saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement}) { + saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement, version}) { if (previousLicenseAgreement) { - return putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement).then(() => { + return putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement, version).then(() => { dispatch({ type: licenseAgreementActionTypes.EDIT_LICENSE_AGREEMENT, licenseAgreement @@ -102,7 +90,7 @@ export default { }); } else { - return postLicenseAgreement(licenseModelId, licenseAgreement).then(response => { + return postLicenseAgreement(licenseModelId, licenseAgreement, version).then(response => { dispatch({ type: licenseAgreementActionTypes.ADD_LICENSE_AGREEMENT, licenseAgreement: { @@ -114,8 +102,8 @@ export default { } }, - deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId}) { - return deleteLicenseAgreement(licenseModelId, licenseAgreementId).then(() => { + deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId, version}) { + return deleteLicenseAgreement(licenseModelId, licenseAgreementId, version).then(() => { dispatch({ type: licenseAgreementActionTypes.DELETE_LICENSE_AGREEMENT, licenseAgreementId @@ -130,31 +118,9 @@ export default { }); }, - 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 deleted file mode 100644 index 42f2407696..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx +++ /dev/null @@ -1,43 +0,0 @@ -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 index af5c454e22..998d5f0e8d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js @@ -1,52 +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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; 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, + SELECT_TAB: null } }); +export const LA_EDITOR_FORM = 'LA_EDITOR_FORM'; + 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 } }); @@ -64,3 +55,11 @@ export const optionsInputValues = { {enum: 'Unlimited', title: 'Unlimited'} ] }; + +export const extractValue = (item) => { + if (item === undefined) { + return ''; + } //TODO fix it later + + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js index 6a3e4dbc73..aada8ddca1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js @@ -1,31 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; import LicenseAgreementEditorView from './LicenseAgreementEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}}) => { - let {data, selectedTab, selectedFeatureGroupsButtonTab} = licenseAgreement.licenseAgreementEditor; + let {data, selectedTab, genericFieldInfo, formReady} = licenseAgreement.licenseAgreementEditor; + const list = licenseAgreement.licenseAgreementList; + const LANames = {}; let previousData; const licenseAgreementId = data ? data.id : null; @@ -33,27 +31,47 @@ export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}} previousData = licenseAgreement.licenseAgreementList.find(licenseAgreement => licenseAgreement.id === licenseAgreementId); } + for (let i = 0; i < list.length; i++) { + LANames[list[i].name] = list[i].id; + } + const {featureGroupsList = []} = featureGroup; + let isFormValid = true; + let invalidTabs = []; + for (let field in genericFieldInfo) { + if (!genericFieldInfo[field].isValid) { + isFormValid = false; + let tabId = genericFieldInfo[field].tabId; + if (invalidTabs.indexOf(tabId) === -1) { + invalidTabs[invalidTabs.length] = genericFieldInfo[field].tabId; + } + } + } + return { data, previousData, selectedTab, - selectedFeatureGroupsButtonTab, - featureGroupsList + featureGroupsList, + LANames, + genericFieldInfo, + isFormValid, + formReady, + invalidTabs }; }; -export const mapActionsToProps = (dispatch, {licenseModelId}) => { +export const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { - onDataChanged: deltaData => LicenseAgreementActionHelper.licenseAgreementEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), 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}); - } + LicenseAgreementActionHelper.saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement, version}); + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js index 74e2f6e8c1..e02935c579 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js @@ -1,40 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes, defaultState} from './LicenseAgreementConstants.js'; +import {actionTypes, defaultState, LA_EDITOR_FORM, enums as LicenseAgreementEnums} from './LicenseAgreementConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.licenseAgreementEditor.OPEN: return { ...state, + formReady: null, + formName: LA_EDITOR_FORM, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + }, + 'requirementsAndConstrains' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + }, + 'licenseTerm' : { + isValid: true, + errorText: '', + validations: [], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + } + }, 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: @@ -42,11 +57,6 @@ export default (state = {}, action) => { ...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 index b21f943fed..67a3333a3a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx @@ -1,17 +1,36 @@ +/*! + * 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. + */ 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 GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Tabs from 'nfvo-components/input/validation/Tabs.jsx'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import Input from 'nfvo-components/input/validation/Input.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 Validator from 'nfvo-utils/Validator.js'; -import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues} from './LicenseAgreementConstants.js'; +import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues, LA_EDITOR_FORM} from './LicenseAgreementConstants.js'; +const dualBoxFilterTitle = { + left: i18n('Available Feature Groups'), + right: i18n('Selected Feature Groups') +}; const LicenseAgreementPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -19,14 +38,77 @@ const LicenseAgreementPropType = React.PropTypes.shape({ description: React.PropTypes.string, requirementsAndConstrains: React.PropTypes.string, licenseTerm: React.PropTypes.object, - featureGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string) + featureGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string), + version: React.PropTypes.object }); + +const GeneralTabContent = ({data, genericFieldInfo, onDataChanged, validateName, validateLTChoice}) => { + let {name, description, requirementsAndConstrains, licenseTerm} = data; + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} + onChange={name => onDataChanged({name}, LA_EDITOR_FORM, { name: validateName })} + label={i18n('Name')} + value={name} + data-test-id='create-la-name' + name='license-agreement-name' + isRequired={true} + type='text'/> + <Input + isValid={genericFieldInfo.requirementsAndConstrains.isValid} + errorText={genericFieldInfo.requirementsAndConstrains.errorText} + onChange={requirementsAndConstrains => onDataChanged({requirementsAndConstrains}, LA_EDITOR_FORM)} + label={i18n('Requirements and Constraints')} + value={requirementsAndConstrains} + data-test-id='create-la-requirements-constants' + name='license-agreement-requirements-and-constraints' + type='textarea'/> + <Input + label={i18n('License Term')} + type='select' + value={licenseTerm && licenseTerm.choice} + isRequired={true} + onChange={e => { + const selectedIndex = e.target.selectedIndex; + const licenseTerm = e.target.options[selectedIndex].value; + onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}}, LA_EDITOR_FORM, { licenseTerm: validateLTChoice }); + }} + isValid={genericFieldInfo.licenseTerm.isValid} + errorText={genericFieldInfo.licenseTerm.errorText} + className='input-options-select' + groupClassName='bootstrap-input-options' + data-test-id='create-la-license-term' > + {LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + <GridItem colSpan={2} stretch> + <Input + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + onChange={description => onDataChanged({description}, LA_EDITOR_FORM)} + label={i18n('Description')} + value={description} + overlayPos='bottom' + data-test-id='create-la-description' + name='license-agreement-description' + isRequired={true} + type='textarea'/> + </GridItem> + </GridSection> + ); +}; + class LicenseAgreementEditorView extends React.Component { static propTypes = { data: LicenseAgreementPropType, previousData: LicenseAgreementPropType, + LANames: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, @@ -42,7 +124,6 @@ class LicenseAgreementEditorView extends React.Component { static defaultProps = { selectedTab: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL, - selectedFeatureGroupsButtonTab: LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS, data: {} }; @@ -51,21 +132,44 @@ class LicenseAgreementEditorView extends React.Component { }; render() { - let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + let {selectedTab, onTabSelect, isReadOnlyMode, featureGroupsList, data, onDataChanged, genericFieldInfo} = 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> + <div> + {genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(LA_EDITOR_FORM) } + className='license-agreement-form'> + <Tabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect} invalidTabs={this.props.invalidTabs} > + <Tab + eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL} + data-test-id='general-tab' + title={i18n('General')}> + <GeneralTabContent data={data} genericFieldInfo={genericFieldInfo} onDataChanged={onDataChanged} validateLTChoice={(value)=>this.validateLTChoice(value)} + validateName={(value)=>this.validateName(value)}/> + </Tab> + <Tab + eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS} + data-test-id='feature-group-tab' + title={i18n('Feature Groups')}> + {featureGroupsList.length > 0 ? + <DualListboxView + isReadOnlyMode={isReadOnlyMode} + filterTitle={dualBoxFilterTitle} + selectedValuesList={data.featureGroupsIds} + availableList={featureGroupsList} + onChange={ selectedValuesList => onDataChanged( { featureGroupsIds: selectedValuesList }, LA_EDITOR_FORM )}/> : + <p>{i18n('There is no available feature groups')}</p>} + </Tab> + </Tabs> + </Form>} + </div> ); } @@ -74,173 +178,19 @@ class LicenseAgreementEditorView extends React.Component { 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> - ); + validateLTChoice(value) { + if (!value.choice) { + return {isValid: false, errorText: i18n('Field is required')}; } + return {isValid: true, errorText: ''}; } - 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 } )}/> - ); - } - } + validateName(value) { + const {data: {id}, LANames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: LANames}); - 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; - } + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('License Agreement by the name \'' + value + '\' already exists. License agreement name must be unique')}; } } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js index ca18bfab79..373694f2bf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js @@ -1,39 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; -import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.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'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; const mapStateToProps = ({licenseModel: {licenseAgreement, licenseModelEditor}}) => { let {licenseAgreementList} = licenseAgreement; let {data} = licenseAgreement.licenseAgreementEditor; - let {vendorName} = licenseModelEditor.data; + let {vendorName, version} = licenseModelEditor.data; let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); return { vendorName, + version, licenseAgreementList, isReadOnlyMode, isDisplayModal: Boolean(data), @@ -43,16 +39,16 @@ const mapStateToProps = ({licenseModel: {licenseAgreement, licenseModelEditor}}) 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) + onAddLicenseAgreementClick: (version) => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, version}), + onEditLicenseAgreementClick: (licenseAgreement, version) => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement, version}), + onDeleteLicenseAgreement: (licenseAgreement, version) => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Are you sure you want to delete "{name}"?', {name: licenseAgreement.name}), + title: i18n('Warning'), + onConfirmed: ()=>LicenseAgreementActionHelper.deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId: licenseAgreement.id, version}) + } + }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx index 4d7e704ba3..776b04b8eb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx @@ -1,15 +1,26 @@ +/*! + * 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. + */ 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'; - +import {extractValue} from './LicenseAgreementConstants'; class LicenseAgreementListEditorView extends React.Component { static propTypes = { @@ -22,7 +33,6 @@ class LicenseAgreementListEditorView extends React.Component { onAddLicenseAgreementClick: React.PropTypes.func, onEditLicenseAgreementClick: React.PropTypes.func, onDeleteLicenseAgreement: React.PropTypes.func, - onCallVCAction: React.PropTypes.func }; static defaultProps = { @@ -34,35 +44,33 @@ class LicenseAgreementListEditorView extends React.Component { }; render() { - const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = 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'> + <ListEditorView + title={i18n('License Agreements', {vendorName})} + plusButtonTitle={i18n('Add License Agreement')} + onAdd={() => onAddLicenseAgreementClick(version)} + filterValue={localFilter} + onFilter={value => this.setState({localFilter: value})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(licenseAgreement => this.renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode, version))} + </ListEditorView> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal 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} /> + <LicenseAgreementEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode} /> ) } </Modal.Body> </Modal> - <LicenseAgreementConfirmationModal licenseModelId={licenseModelId}/> - </div> ); } @@ -73,7 +81,7 @@ class LicenseAgreementListEditorView extends React.Component { 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); + return escape(name).match(filter) || escape(description).match(filter) || escape(extractValue(licenseTerm)).match(filter); }); } else { @@ -81,14 +89,14 @@ class LicenseAgreementListEditorView extends React.Component { } } - renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode) { + renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode, version) { let {id, name, description, licenseTerm, featureGroupsIds = []} = licenseAgreement; let {onEditLicenseAgreementClick, onDeleteLicenseAgreement} = this.props; return ( <ListEditorItemView key={id} - onSelect={() => onEditLicenseAgreementClick(licenseAgreement)} - onDelete={() => onDeleteLicenseAgreement(licenseAgreement)} + onSelect={() => onEditLicenseAgreementClick(licenseAgreement, version)} + onDelete={() => onDeleteLicenseAgreement(licenseAgreement, version)} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode}> <div className='list-editor-item-view-field'> @@ -98,7 +106,7 @@ class LicenseAgreementListEditorView extends React.Component { <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 className='text type'>{extractValue(licenseTerm)}</div> </div> <div className='list-editor-item-view-field-tight'> <div className='title'>{i18n('Feature')}</div> @@ -113,14 +121,6 @@ class LicenseAgreementListEditorView extends React.Component { </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 index 5b5fa00df1..e6a8f34b58 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js index 50ac2c85a3..dd2a5c6003 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js @@ -1,44 +1,39 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-key-groups`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/license-key-groups`; } function fetchLicenseKeyGroupsList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseKeyGroupId}`); +function deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${licenseKeyGroupId}`); } -function postLicenseKeyGroup(licenseModelId, licenseKeyGroup) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function postLicenseKeyGroup(licenseModelId, licenseKeyGroup, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: licenseKeyGroup.name, description: licenseKeyGroup.description, operationalScope: licenseKeyGroup.operationalScope, @@ -46,8 +41,8 @@ function postLicenseKeyGroup(licenseModelId, licenseKeyGroup) { }); } -function putLicenseKeyGroup(licenseModelId, licenseKeyGroup) { - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseKeyGroup.id}`, { +function putLicenseKeyGroup(licenseModelId, licenseKeyGroup, version) { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${licenseKeyGroup.id}`, { name: licenseKeyGroup.name, description: licenseKeyGroup.description, operationalScope: licenseKeyGroup.operationalScope, @@ -77,9 +72,9 @@ export default { }); }, - saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}) { + saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup, version}) { if (previousLicenseKeyGroup) { - return putLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(() => { + return putLicenseKeyGroup(licenseModelId, licenseKeyGroup, version).then(() => { dispatch({ type: licenseKeyGroupsConstants.EDIT_LICENSE_KEY_GROUP, licenseKeyGroup @@ -87,11 +82,12 @@ export default { }); } else { - return postLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(response => { + return postLicenseKeyGroup(licenseModelId, licenseKeyGroup, version).then(response => { dispatch({ type: licenseKeyGroupsConstants.ADD_LICENSE_KEY_GROUP, licenseKeyGroup: { ...licenseKeyGroup, + referencingFeatureGroups: [], id: response.value } }); @@ -101,8 +97,8 @@ export default { }, - deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId}){ - return deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId).then(()=> { + deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId, version}){ + return deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId, version).then(()=> { dispatch({ type: licenseKeyGroupsConstants.DELETE_LICENSE_KEY_GROUP, licenseKeyGroupId @@ -110,13 +106,6 @@ export default { }); }, - licenseKeyGroupEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.DATA_CHANGED, - deltaData - }); - }, - hideDeleteConfirm(dispatch) { dispatch({ type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM, diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx deleted file mode 100644 index 2413db51d0..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx +++ /dev/null @@ -1,49 +0,0 @@ -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 index d32bc52744..50d1fe8625 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js @@ -1,25 +1,21 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; export const actionTypes = keyMirror({ @@ -42,6 +38,8 @@ export const defaultState = { } }; +export const LKG_FORM_NAME = 'LKGFORM'; + export const optionsInputValues = { OPERATIONAL_SCOPE: [ {enum: '', title: i18n('please select…')}, @@ -61,4 +59,21 @@ export const optionsInputValues = { ] }; +export const extractValue = (item) => { + if (item === undefined) {return '';} //TODO fix it later + + return item ? item === optionInputOther.OTHER ? item : InputOptions.getTitleByName(optionsInputValues, item) : ''; +}; +export const 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; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js index 3940ec594a..aef1532dc1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js @@ -1,52 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; import LicenseKeyGroupsEditorView from './LicenseKeyGroupsEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => { - let {data} = licenseKeyGroup.licenseKeyGroupsEditor; + let {data, genericFieldInfo, formReady} = licenseKeyGroup.licenseKeyGroupsEditor; - let previousData; + let previousData, LKGNames = {}; const licenseKeyGroupId = data ? data.id : null; if(licenseKeyGroupId) { previousData = licenseKeyGroup.licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId); } + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + const list = licenseKeyGroup.licenseKeyGroupsList; + for (let i = 0; i < list.length; i++) { + LKGNames[list[i].name] = list[i].id; + } + return { data, - previousData + previousData, + genericFieldInfo, + isFormValid, + formReady, + LKGNames }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { - onDataChanged: deltaData => LicenseKeyGroupsActionHelper.licenseKeyGroupEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch), onSubmit: ({previousLicenseKeyGroup, licenseKeyGroup}) => { LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch); - LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}); - } + LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup, version}); + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js index a74498269a..090c971c65 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js @@ -1,42 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes, defaultState} from './LicenseKeyGroupsConstants.js'; +import {actionTypes, defaultState, LKG_FORM_NAME} from './LicenseKeyGroupsConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.licenseKeyGroupsEditor.OPEN: return { ...state, - data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor + data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor, + formReady: null, + formName: LKG_FORM_NAME, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'type' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'operationalScope' : { + isValid: true, + errorText: '', + validations: [] + } + } }; 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 index 102e713060..b95efd0f9c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx @@ -1,10 +1,29 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.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'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {optionsInputValues as licenseKeyGroupOptionsInputValues, LKG_FORM_NAME} from './LicenseKeyGroupsConstants.js'; +import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; const LicenseKeyGroupPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -17,10 +36,80 @@ const LicenseKeyGroupPropType = React.PropTypes.shape({ type: React.PropTypes.string }); +const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, validateName, validateOperationalScope}) => { + let {name, description, operationalScope, type} = data; + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name}, LKG_FORM_NAME, {name: validateName})} + label={i18n('Name')} + data-test-id='create-lkg-name' + value={name} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} + isRequired={true} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={true} + isRequired={true} + onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}}, + LKG_FORM_NAME, {operationalScope: validateOperationalScope})} + onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], + other: operationalScope}}, LKG_FORM_NAME, {operationalScope: validateOperationalScope})} + label={i18n('Operational Scope')} + data-test-id='create-lkg-operational-scope' + type='select' + multiSelectedEnum={operationalScope && operationalScope.choices} + otherValue={operationalScope && operationalScope.other} + values={licenseKeyGroupOptionsInputValues.OPERATIONAL_SCOPE} + isValid={genericFieldInfo.operationalScope.isValid} + errorText={genericFieldInfo.operationalScope.errorText} /> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={description => onDataChanged({description}, LKG_FORM_NAME)} + label={i18n('Description')} + data-test-id='create-lkg-description' + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + isRequired={true} + type='textarea' + overlayPos='bottom' /> + </GridItem> + <GridItem colSpan={2}> + <Input + isRequired={true} + onChange={e => { const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({type: val}, LKG_FORM_NAME);}} + value={type} + label={i18n('Type')} + data-test-id='create-lkg-type' + isValid={genericFieldInfo.type.isValid} + errorText={genericFieldInfo.type.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + { + licenseKeyGroupOptionsInputValues.TYPE.map(type => + (<option key={type.enum} value={type.enum}>{type.title}</option>)) + } + </Input> + </GridItem> + </GridSection> + ); +}; + class LicenseKeyGroupsEditorView extends React.Component { static propTypes = { data: LicenseKeyGroupPropType, previousData: LicenseKeyGroupPropType, + LKGNames: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, @@ -32,54 +121,29 @@ class LicenseKeyGroupsEditorView extends React.Component { }; render() { - let {data = {}, onDataChanged, isReadOnlyMode} = this.props; - let {name, description, operationalScope, type} = data; + let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo} = this.props; return ( - <ValidationForm + <div> + { genericFieldInfo && + <Form ref='validationForm' hasButtons={true} onSubmit={ () => this.submit() } onReset={ () => this.props.onCancel() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(LKG_FORM_NAME) } 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> + <LicenseKeyGroupFormContent + data={data} + onDataChanged={onDataChanged} + genericFieldInfo={genericFieldInfo} + validateName={(value)=> this.validateName(value)} + validateOperationalScope={this.validateOperationalScope}/> + </Form>} + </div> ); } @@ -87,6 +151,37 @@ class LicenseKeyGroupsEditorView extends React.Component { const {data: licenseKeyGroup, previousData: previousLicenseKeyGroup} = this.props; this.props.onSubmit({licenseKeyGroup, previousLicenseKeyGroup}); } + + validateName(value) { + const {data: {id}, LKGNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: LKGNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('License key group by the name \'' + value + '\' already exists. License key group name must be unique')}; + } + + validateOperationalScope(value) { + if (value && value.choices && value.choices.length > 0) { + if (value.choices[0] !== optionInputOther.OTHER) + { + return { + isValid: true, + errorText: '' + }; + } else { + if ( value.other ) { + return { + isValid: true, + errorText: '' + }; + } + } + } + return { + isValid: false, + errorText: 'Field is required' + }; + } } 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 index e1b610f973..e2c6c30e21 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js @@ -1,27 +1,24 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; -import LicenseKeyGroupsListEditorView from './LicenseKeyGroupsListEditorView.jsx'; +import LicenseKeyGroupsListEditorView, {generateConfirmationMsg} from './LicenseKeyGroupsListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) => { let {licenseKeyGroupsList} = licenseKeyGroup; @@ -38,11 +35,18 @@ const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) }; }; -const mapActionsToProps = (dispatch) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onAddLicenseKeyGroupClick: () => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch), onEditLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup}), - onDeleteLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseKeyGroup}) + onDeleteLicenseKeyGroupClick: licenseKeyGroup => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateConfirmationMsg(licenseKeyGroup), + title: i18n('Warning'), + onConfirmed: ()=>LicenseKeyGroupsActionHelper.deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId:licenseKeyGroup.id, version}) + } + }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx index 1ed1d2093a..a303e46706 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -8,8 +23,6 @@ import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.js 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 = { @@ -33,35 +46,33 @@ class LicenseKeyGroupsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = 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})} + title={i18n('License Key Groups', {vendorName})} plusButtonTitle={i18n('Add License Key Group')} onAdd={onAddLicenseKeyGroupClick} filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} + onFilter={value => this.setState({localFilter: value})} isReadOnlyMode={isReadOnlyMode}> {this.filterList().map(licenseKeyGroup => (this.renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode)))} </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='license-key-groups-modal'> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal 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}/> + <LicenseKeyGroupsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/> ) } </Modal.Body> </Modal> - <LicenseKeyGroupsConfirmationModal licenseModelId={licenseModelId}/> - </div> ); } @@ -134,5 +145,17 @@ class LicenseKeyGroupsListEditorView extends React.Component { export default LicenseKeyGroupsListEditorView; - - +export function generateConfirmationMsg(licenseKeyGroupToDelete) { + let name = licenseKeyGroupToDelete ? licenseKeyGroupToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let subMsg = 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> + ); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js index 54ce4e3955..1f0a64e295 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './LicenseKeyGroupsConstants.js'; export default (state = [], action) => { switch (action.type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverview.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverview.js new file mode 100644 index 0000000000..1ca4f37988 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverview.js @@ -0,0 +1,163 @@ +/*! + * 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. + */ +import {connect} from 'react-redux'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import LicenseModelOverviewView from './LicenseModelOverviewView.jsx'; +import {overviewEditorHeaders, selectedButton} from './LicenseModelOverviewConstants.js'; +import licenseModelOverviewActionHelper from './licenseModelOverviewActionHelper.js'; + +export const mapStateToProps = ({licenseModel: {licenseModelEditor, entitlementPool, licenseAgreement, featureGroup, licenseKeyGroup, licenseModelOverview}}) => { + + let modalHeader, licensingDataList; + let isDisplayModal = false; + + const reduceLicenseKeyGroups = (accum, licenseKeyGroupId) => { + let curLicenseKeyGroup = licenseKeyGroup.licenseKeyGroupsList.find(item => {return item.id === licenseKeyGroupId;}); + if (curLicenseKeyGroup) { + accum.push({ + ...curLicenseKeyGroup, + itemType: overviewEditorHeaders.LICENSE_KEY_GROUP + }); + } + return accum; + }; + + const reduceEntitlementPools = (accum, entitlementPoolId) => { + let curEntitlementPool = entitlementPool.entitlementPoolsList.find(item => {return item.id === entitlementPoolId;}); + if (curEntitlementPool) { + accum.push ({ + ...curEntitlementPool, + itemType: overviewEditorHeaders.ENTITLEMENT_POOL + }); + } + return accum; + }; + + const reduceFeatureGroups = (accum, featureGroupId) => { + let curFeatureGroup = featureGroup.featureGroupsList.find(item => {return item.id === featureGroupId;}); + if (curFeatureGroup) { + let {entitlementPoolsIds = [], licenseKeyGroupsIds = []} = curFeatureGroup; + accum.push({ + ...curFeatureGroup, + itemType: overviewEditorHeaders.FEATURE_GROUP, + children: [ + ...entitlementPoolsIds.length ? entitlementPoolsIds.reduce(reduceEntitlementPools, []) : [], + ...licenseKeyGroupsIds.length ? licenseKeyGroupsIds.reduce(reduceLicenseKeyGroups, []) : [] + ] + }); + } + return accum; + }; + + + const checkEP = (accum, elem) => { + if (!elem.referencingFeatureGroups || !elem.referencingFeatureGroups.length) { + accum.push({ + ...elem, + itemType: overviewEditorHeaders.ENTITLEMENT_POOL + }); + } + return accum; + }; + + const checkLG = (accum, elem) => { + if (!elem.referencingFeatureGroups || !elem.referencingFeatureGroups.length) { + accum.push({ + ...elem, + itemType: overviewEditorHeaders.LICENSE_KEY_GROUP + }); + } + return accum; + }; + + const checkFG = (accum, elem) => { + if (!elem.referencingLicenseAgreements || !elem.referencingLicenseAgreements.length) { + let {entitlementPoolsIds = [], licenseKeyGroupsIds = []} = elem; + accum.push({ + ...elem, + itemType: overviewEditorHeaders.FEATURE_GROUP, + + children: [ + ...entitlementPoolsIds.length ? entitlementPoolsIds.reduce(reduceEntitlementPools, []) : [], + ...licenseKeyGroupsIds.length ? licenseKeyGroupsIds.reduce(reduceLicenseKeyGroups, []) : [] + ] + + }); + } + return accum; + }; + + + + const mapLicenseAgreementData = licenseAgreement => { + let {featureGroupsIds = []} = licenseAgreement; + return { + ...licenseAgreement, + itemType: overviewEditorHeaders.LICENSE_AGREEMENT, + children: featureGroupsIds.length ? featureGroupsIds.reduce(reduceFeatureGroups, []) : [] + }; + }; + + if (entitlementPool.entitlementPoolEditor && entitlementPool.entitlementPoolEditor.data) { + modalHeader = overviewEditorHeaders.ENTITLEMENT_POOL; + isDisplayModal = true; + }else + if (licenseAgreement.licenseAgreementEditor && licenseAgreement.licenseAgreementEditor.data) { + modalHeader = overviewEditorHeaders.LICENSE_AGREEMENT; + isDisplayModal = true; + }else + if (featureGroup.featureGroupEditor && featureGroup.featureGroupEditor.data) { + modalHeader = overviewEditorHeaders.FEATURE_GROUP; + isDisplayModal = true; + }else + if (licenseKeyGroup.licenseKeyGroupsEditor && licenseKeyGroup.licenseKeyGroupsEditor.data) { + modalHeader = overviewEditorHeaders.LICENSE_KEY_GROUP; + isDisplayModal = true; + } + + if (licenseModelOverview.selectedTab === selectedButton.NOT_IN_USE) { + licensingDataList = [ + ...featureGroup.featureGroupsList.reduce(checkFG, []), + ...entitlementPool.entitlementPoolsList.reduce(checkEP, []), + ...licenseKeyGroup.licenseKeyGroupsList.reduce(checkLG, []) + ]; + }else { + licensingDataList = licenseAgreement.licenseAgreementList && licenseAgreement.licenseAgreementList.length ? licenseAgreement.licenseAgreementList.map(mapLicenseAgreementData) : []; + } + + return { + isReadOnlyMode: VersionControllerUtils.isReadOnly(licenseModelEditor.data), + isDisplayModal, + modalHeader, + licenseModelId: licenseModelEditor.data.id, + version: licenseModelEditor.data.version, + licensingDataList, + selectedTab: licenseModelOverview.selectedTab + + }; +}; + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onCallVCAction: action => { + LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}); + }, + onTabSelect: (buttonTab) => licenseModelOverviewActionHelper.selectVLMListView(dispatch,{buttonTab}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseModelOverviewView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js new file mode 100644 index 0000000000..b5a27ed018 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js @@ -0,0 +1,42 @@ +/*! + * 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. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const overviewItems = keyMirror({ + LICENSE_AGREEMENTS: 'License Agreements', + FEATURE_GROUPS: 'Feature Groups', + ENTITLEMENT_POOLS: 'Entitlement Pools', + LICENSE_KEY_GROUPS: 'License Key Groups' +}); + +export const overviewEditorHeaders = keyMirror({ + LICENSE_AGREEMENT: 'License Agreement', + FEATURE_GROUP: 'Feature Group', + ENTITLEMENT_POOL: 'Entitlement Pool', + LICENSE_KEY_GROUP: 'License Key Group' +}); + +export const actionTypes = keyMirror({ + LICENSE_MODEL_OVERVIEW_TAB_SELECTED: null, + LM_DATA_CHANGED: null +}); + +export const selectedButton = keyMirror({ + VLM_LIST_VIEW: null, + NOT_IN_USE: null +}); + +export const VLM_DESCRIPTION_FORM = 'VLMDEWSCRIPTIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx new file mode 100644 index 0000000000..d6c79ddb52 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx @@ -0,0 +1,105 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import classNames from 'classnames'; + +import EntitlementPoolsEditor from '../entitlementPools/EntitlementPoolsEditor.js'; +import FeatureGroupEditor from '../featureGroups/FeatureGroupEditor.js'; +import LicenseAgreementEditor from '../licenseAgreement/LicenseAgreementEditor.js'; +import LicenseKeyGroupsEditor from '../licenseKeyGroups/LicenseKeyGroupsEditor.js'; +import {overviewEditorHeaders, selectedButton} from './LicenseModelOverviewConstants.js'; + +import SummaryView from './SummaryView.jsx'; +import VLMListView from './VLMListView.jsx'; +import ListButtons from './summary/ListButtons.jsx'; + + +const setModalClassName = (modalHeader) => { + switch (modalHeader) { + case overviewEditorHeaders.ENTITLEMENT_POOL: + return 'entitlement-pools-modal'; + case overviewEditorHeaders.LICENSE_AGREEMENT: + return 'license-agreement-modal'; + case overviewEditorHeaders.FEATURE_GROUP: + return 'feature-group-modal'; + case overviewEditorHeaders.LICENSE_KEY_GROUP: + return 'license-key-groups-modal'; + default: + return ''; + } +}; + +class LicenseModelOverviewView extends React.Component { + + static propTypes = { + isDisplayModal: React.PropTypes.bool, + isReadOnlyMode: React.PropTypes.bool, + licenseModelId: React.PropTypes.string, + licensingDataList: React.PropTypes.array, + modalHeader: React.PropTypes.string, + selectedTab: React.PropTypes.symbol, + onTabSelect: React.PropTypes.func, + onCallVCAction: React.PropTypes.func, + onClose: React.PropTypes.func + }; + + render() { + let {isDisplayModal, modalHeader, licensingDataList, selectedTab, onTabSelect} = this.props; + let selectedInUse = selectedTab !== selectedButton.NOT_IN_USE; + + return( + <div className='license-model-overview'> + <SummaryView/> + <div className={classNames('overview-list-section ', !selectedInUse ? 'overview-list-orphans' : '' )}> + <div className='vlm-list-tab-panel'> + <div className='section-title'>{selectedInUse ? i18n('VLM List View') : i18n('Entities not in Use')}</div> + <ListButtons onTabSelect={onTabSelect} selectedInUse={selectedInUse}/> + </div> + <VLMListView licensingDataList={licensingDataList} showInUse={selectedInUse}/> + </div> + { + isDisplayModal && + <Modal show={isDisplayModal} bsSize='large' animation={true} className={classNames('onborading-modal', setModalClassName(modalHeader))}> + <Modal.Header> + <Modal.Title>{`${i18n('Create New ')}${i18n(modalHeader)}`}</Modal.Title> + </Modal.Header> + <Modal.Body> + {this.renderModalBody(modalHeader)} + </Modal.Body> + </Modal> + } + </div> + ); + } + + renderModalBody(modalHeader) { + let {licenseModelId, version} = this.props; + switch (modalHeader) { + case overviewEditorHeaders.ENTITLEMENT_POOL: + return <EntitlementPoolsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + case overviewEditorHeaders.LICENSE_AGREEMENT: + return <LicenseAgreementEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + case overviewEditorHeaders.FEATURE_GROUP: + return <FeatureGroupEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + case overviewEditorHeaders.LICENSE_KEY_GROUP: + return <LicenseKeyGroupsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + } + } +} + +export default LicenseModelOverviewView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/SummaryView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/SummaryView.jsx new file mode 100644 index 0000000000..6fcdb477e6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/SummaryView.jsx @@ -0,0 +1,33 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {default as VendorDataView} from './summary/VendorDataView.js'; +import {default as SummaryCountList} from './summary/SummaryCountList.js'; + +function SummaryView() { + return( + <div className='overview-top-section'> + <div className='overview-title'>{i18n('overview')}</div> + <div className='license-model-overview-top'> + <VendorDataView/> + <SummaryCountList/> + </div> + </div> + ); +} + +export default SummaryView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx new file mode 100644 index 0000000000..119008a849 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx @@ -0,0 +1,123 @@ +/*! + * 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. + */ +import React, {Component} from 'react'; +import {Collapse} from 'react-bootstrap'; +import LicenseAgreement from './listItems/LicenseAgreement.jsx'; +import EntitlementPool from './listItems/EntitlementPool.jsx'; +import FeatureGroup from './listItems/FeatureGroup.jsx'; +import LicenseKeyGroup from './listItems/LicenseKeyGroup.jsx'; +import {overviewEditorHeaders} from './LicenseModelOverviewConstants.js'; + +class VLMListView extends Component { + + static propTypes = { + licensingDataList: React.PropTypes.array, + showInUse: React.PropTypes.bool + }; + + state = { + + }; + + render() { + let {licensingDataList = []} = this.props; + return ( + <div className='vlm-list-view'> + <div> + <ul className='vlm-list' data-test-id='vlm-list'> + {licensingDataList.map(item => this.renderLicensingItem(item))} + </ul> + </div> + </div> + ); + } + + renderLicensingItem(item) { + switch (item.itemType) { + case overviewEditorHeaders.LICENSE_AGREEMENT : + return this.renderLicenseAgreementItem(item); + case overviewEditorHeaders.FEATURE_GROUP : + return this.renderFeatureGroupItem(item); + case overviewEditorHeaders.LICENSE_KEY_GROUP : + return this.renderLicenseKeyGroupItem(item); + case overviewEditorHeaders.ENTITLEMENT_POOL: + return this.renderEntitlementPoolItem(item); + default: + return; + } + } + + renderLicenseAgreementItem(licenseAgreement) { + return ( + <li key={licenseAgreement.id}> + <LicenseAgreement + laData={licenseAgreement} + isCollapsed={this.state[licenseAgreement.id]} + onClick={event => this.updateCollapsable(event, licenseAgreement.id) }/> + <Collapse in={this.state[licenseAgreement.id]}> + <ul> + {licenseAgreement.children && licenseAgreement.children.map(item => this.renderLicensingItem(item))} + </ul> + </Collapse> + </li> + ); + } + + renderFeatureGroupItem(featureGroup) { + const {showInUse} = this.props; + return ( + <li key={featureGroup.id}> + <FeatureGroup + fgData={featureGroup} + isCollapsed={this.state[featureGroup.id]} + onClick={event=> this.updateCollapsable(event, featureGroup.id) }/> + { + showInUse && <Collapse in={this.state[featureGroup.id]}> + <ul> + {featureGroup.children && featureGroup.children.map(item => this.renderLicensingItem(item))} + + </ul> + </Collapse> + } + </li> + ); + } + + renderEntitlementPoolItem(entitlementPool) { + return ( + <li key={entitlementPool.id}> + <EntitlementPool epData={entitlementPool} /> + </li> + ); + } + + renderLicenseKeyGroupItem(licenseKeyGroup) { + return ( + <li key={licenseKeyGroup.id}> + <LicenseKeyGroup lkgData={licenseKeyGroup} /> + </li> + ); + } + + updateCollapsable(event, id) { + event.stopPropagation(); + let obj = {}; + obj[id] = !this.state[id]; + this.setState(obj); + } +} + +export default VLMListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js new file mode 100644 index 0000000000..f0286ba3bb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js @@ -0,0 +1,39 @@ +/*! + * 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. + */ +import {actionTypes} from './LicenseModelOverviewConstants.js'; + +export default { + selectVLMListView(dispatch, {buttonTab}) { + dispatch({ + type: actionTypes.LICENSE_MODEL_OVERVIEW_TAB_SELECTED, + buttonTab + }); + }, + + editDescriptionOpen(dispatch, {description}) { + dispatch({ + type: actionTypes.LM_DATA_CHANGED, + description + }); + }, + + editDescriptionClose(dispatch) { + dispatch({ + type: actionTypes.LM_DATA_CHANGED, + description: false + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx new file mode 100644 index 0000000000..94977b40d1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx @@ -0,0 +1,53 @@ +/*! + * 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. + */ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {extractValue, extractUnits} from '../../entitlementPools/EntitlementPoolsConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; + +class EntitlementPool extends Component { + render() { + let {epData: {name, description, manufacturerReferenceNumber}} = this.props; + return ( + <div className='vlm-list-item vlm-list-item-ep' data-test-id='vlm-list-item-ep'> + <ArrowCol/> + <IconCol className='ep-icon'/> + <ItemInfo name={name} description={description}/> + <AdditionalDataCol> + <AdditionalDataElement + className='vlm-list-item-entitlement-metric' + name={i18n('Entitlement')} + value={this.getEntitlement()}/> + <AdditionalDataElement + name={i18n('Manufacturer Reference Number')} + value={manufacturerReferenceNumber} + className='vlm-list-item-sku'/> + </AdditionalDataCol> + </div> + ); + } + + getEntitlement() { + let {epData: {entitlementMetric, aggregationFunction, time, thresholdValue, thresholdUnits}} = this.props; + return `${extractValue(aggregationFunction)} ${extractValue(entitlementMetric)} per ${extractValue(time)} ${thresholdValue} ${extractUnits(thresholdUnits)}`; + } + +} + +export default EntitlementPool; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx new file mode 100644 index 0000000000..8dbd46a29e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx @@ -0,0 +1,50 @@ +/*! + * 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. + */ +import React, {Component} from 'react'; +import {overviewEditorHeaders} from '../LicenseModelOverviewConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; + +class FeatureGroup extends Component { + render() { + let {fgData: {name, description, children = []}, isCollapsed, onClick} = this.props; + return ( + <div onClick={e => onClick(e)} className='vlm-list-item vlm-list-item-fg' data-test-id='vlm-list-item-fg'> + <ArrowCol isCollapsed={isCollapsed} length={children.length} /> + <IconCol className='fg-icon'/> + <ItemInfo name={name} description={description}> + <div className='children-count'> + <span className='count-value'> + Entitlement Pools: + <span data-test-id='vlm-list-ep-count-value'> + {`${children.filter(child => child.itemType === overviewEditorHeaders.ENTITLEMENT_POOL).length}`} + </span> + </span> + <span className='count-value'> + License Key Groups: + <span data-test-id='vlm-list-lkg-count-value'> + {`${children.filter(child => child.itemType === overviewEditorHeaders.LICENSE_KEY_GROUP).length}`} + </span> + </span> + </div> + </ItemInfo> + </div> + ); + } +} + +export default FeatureGroup; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx new file mode 100644 index 0000000000..dd4686d330 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx @@ -0,0 +1,53 @@ +/*! + * 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. + */ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import {optionsInputValues} from '../../licenseAgreement/LicenseAgreementConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; + +class LicenseAgreement extends Component { + render() { + let {laData: {name, description, licenseTerm, children = []}, isCollapsed, onClick} = this.props; + return ( + <div onClick={e => onClick(e)} className='vlm-list-item vlm-list-item-la' data-test-id='vlm-list-la-item'> + <ArrowCol isCollapsed={isCollapsed} length={children.length} /> + <IconCol className='la-icon'/> + <ItemInfo name={name} description={description}> + <div className='children-count'> + <span className='count-value'>Feature Groups: <span data-test-id='vlm-list-fg-count-value'>{`${children.length}`}</span></span> + </div> + </ItemInfo> + <AdditionalDataCol> + <AdditionalDataElement + name={i18n('License Model Type')} + value={this.extractValue(licenseTerm)}/> + </AdditionalDataCol> + </div> + ); + } + + 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 LicenseAgreement; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx new file mode 100644 index 0000000000..9722b83336 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx @@ -0,0 +1,48 @@ +/*! + * 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. + */ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {extractValue, getOperationalScopes} from '../../licenseKeyGroups/LicenseKeyGroupsConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; + +class LicenseKeyGroup extends Component { + render() { + let {lkgData: {name, description, operationalScope, type}} = this.props; + return ( + <div className='vlm-list-item vlm-list-item-lkg' data-test-id='vlm-list-item-lkg'> + <ArrowCol/> + <IconCol className='lkg-icon'/> + <ItemInfo name={name} description={description}/> + <AdditionalDataCol> + <AdditionalDataElement + className='vlm-list-item-operational-scope' + name={i18n('Operational Scope')} + value={operationalScope && getOperationalScopes(operationalScope)}/> + <AdditionalDataElement + className='vlm-list-item-group-type' + name={i18n('Type')} + value={extractValue(type)}/> + </AdditionalDataCol> + </div> + ); + } + +} + +export default LicenseKeyGroup; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/AdditionalDataCol.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/AdditionalDataCol.jsx new file mode 100644 index 0000000000..5b5daafb4f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/AdditionalDataCol.jsx @@ -0,0 +1,51 @@ +/*! + * 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. + */ +import React from 'react'; + +function AdditionalDataCol({children}) { + return ( + <div className='list-item-section list-item-additional-data-col'> + <div className='additional-data-col-border'></div> + <div className='additional-data'> + {children} + </div> + </div> + ); +} + +AdditionalDataCol.propTypes = { + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.node), + React.PropTypes.node + ]) +}; + +function AdditionalDataElement({className, name, value}) { + return ( + <div className={className}> + <span className='additional-data-name'>{name}: </span> + <span className='additional-data-value'>{value}</span> + </div> + ); +} + +AdditionalDataElement.propTypes = { + name: React.PropTypes.string, + value: React.PropTypes.string, + className: React.PropTypes.string +}; + +export {AdditionalDataCol, AdditionalDataElement}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ArrowCol.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ArrowCol.jsx new file mode 100644 index 0000000000..a5eb9d27dd --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ArrowCol.jsx @@ -0,0 +1,35 @@ +/*! + * 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. + */ +import React from 'react'; +import classNames from 'classnames'; + +function ArrowCol ({isCollapsed, length}) { + return ( + <div className='list-item-section list-item-arrow-col'> + <div className={classNames('arrow-icon', {'chevron': length > 0}, + {'down': (length > 0 && isCollapsed)}, + {'right': (length > 0 && (!isCollapsed))})} > + </div> + </div> + ); +} + +ArrowCol.propTypes = { + isCollapsed: React.PropTypes.bool, + length: React.PropTypes.number +}; + +export default ArrowCol; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/IconCol.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/IconCol.jsx new file mode 100644 index 0000000000..7fd7fcb88a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/IconCol.jsx @@ -0,0 +1,26 @@ +/*! + * 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. + */ +import React from 'react'; + +function IconCol({className}) { + return ( + <div className='list-item-section list-item-icon-col'> + <div className={className}></div> + </div> + ); +} + +export default IconCol; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ItemInfo.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ItemInfo.jsx new file mode 100644 index 0000000000..655a0dd4a8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ItemInfo.jsx @@ -0,0 +1,39 @@ +/*! + * 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. + */ +import React from 'react'; + +function ItemInfo({name, description, children}) { + return ( + <div className='list-item-section vlm-item-info'> + <div className='vlm-list-item-title'> + <div className='item-name' data-test-id='vlm-list-item-name'>{name}</div> + {children} + </div> + <div className='vlm-list-item-description'>{description}</div> + </div> + ); +} + +ItemInfo.propTypes = { + name: React.PropTypes.string, + description: React.PropTypes.string, + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.node), + React.PropTypes.node + ]) +}; + +export default ItemInfo; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx new file mode 100644 index 0000000000..0c0103fc10 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx @@ -0,0 +1,56 @@ +/*! + * 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. + */ +import React from 'react'; +import Input from 'nfvo-components/input/validation/Input.jsx'; + +class LicenseModelDescriptionEdit extends React.Component { + render() { + let {onDataChanged, description, genericFieldInfo} = this.props; + let saveButtonClassName = (genericFieldInfo.description.isValid) ? 'description-save' : 'description-save disabled'; + return( + <div className='vendor-description-edit'> + + <Input + onChange={description => onDataChanged({description})} + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + className='description-edit-textarea' + type='textarea'/> + <div className='buttons-row'> + <div className='buttons-wrapper'> + <div onClick={() => this.onClose()} className='description-button' data-test-id='vlm-summary-vendor-desc-cancel-btn'>cancel</div> + <div onClick={() => this.submit()} className={saveButtonClassName} data-test-id='vlm-summary-vendor-desc-save-btn'>save</div> + </div> + </div> + </div> + ); + } + + onClose() { + this.props.onClose(); + } + + submit() { + let {onSubmit, data, description} = this.props; + onSubmit({ + ...data, + description: description + }); + } +} + +export default LicenseModelDescriptionEdit; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx new file mode 100644 index 0000000000..730ccb33f1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx @@ -0,0 +1,39 @@ +/*! + * 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. + */ +import React from 'react'; +import {selectedButton} from '../LicenseModelOverviewConstants.js'; + +function ListButtons ({onTabSelect, selectedInUse}) { + return ( + <div className='overview-buttons-section'> + <div onClick={()=>onTabSelect(selectedButton.VLM_LIST_VIEW)} + className={selectedInUse ? 'button-vlm-list-view vlm-list-icon selected' : 'button-vlm-list-view vlm-list-icon' } + data-test-id='vlm-overview-vlmlist-tab'></div> + <div onClick={()=>onTabSelect(selectedButton.NOT_IN_USE)} + className={selectedInUse ? 'button-vlm-list-view entities-list-icon' : 'button-vlm-list-view entities-list-icon selected' } + data-test-id='vlm-overview-orphans-tab' > + </div> + + </div> + ); +} + +ListButtons.propTypes = { + onTabSelect: React.PropTypes.func, + selectedInUse: React.PropTypes.bool +}; + +export default ListButtons; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx new file mode 100644 index 0000000000..66f2cc6838 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx @@ -0,0 +1,31 @@ +/*! + * 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. + */ +import React from 'react'; + +function SummaryCountItem ({name, counter, onAdd, onNavigate, isReadOnlyMode}) { + return( + <div className='summary-count-item'> + <div className='summary-name-and-count' onClick={onNavigate}> + <span className='item-name' onClick={onNavigate}>{name}</span> + <span className='item-count' onClick={onNavigate} data-test-id={'vlm-summary-vendor-counter-' + name.toLowerCase().replace(/\s/g,'-')}>({counter})</span> + </div> + <div className={isReadOnlyMode ? 'add-button disabled' : 'add-button'} onClick={onAdd} data-test-id={'vlm-summary-vendor-add-btn-' + name.toLowerCase().replace(/\s/g,'-')}/> + </div> + ); +} + +export default SummaryCountItem; + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountList.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountList.js new file mode 100644 index 0000000000..c69a092d23 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountList.js @@ -0,0 +1,126 @@ +/*! + * 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. + */ +import React from 'react'; +import {connect} from 'react-redux'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import OnboardingActionHelper from '../../../OnboardingActionHelper.js'; +import EntitlementPoolsActionHelper from '../../entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseAgreementActionHelper from '../../licenseAgreement/LicenseAgreementActionHelper.js'; +import LicenseKeyGroupsActionHelper from '../../licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; +import FeatureGroupsActionHelper from '../../featureGroups/FeatureGroupsActionHelper.js'; + +import {overviewItems} from '../LicenseModelOverviewConstants.js'; +import SummaryCountItem from './SummaryCountItem.jsx'; + +export const mapStateToProps = ({licenseModel: {licenseModelEditor, licenseAgreement: {licenseAgreementList}, + featureGroup: {featureGroupsList}, entitlementPool: {entitlementPoolsList}, licenseKeyGroup: {licenseKeyGroupsList}}}) => { + + let {vendorName, description, id, version} = licenseModelEditor.data; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + let counts = [ + {name: overviewItems.LICENSE_AGREEMENTS, count: licenseAgreementList.length}, + {name: overviewItems.FEATURE_GROUPS, count: featureGroupsList.length}, + {name: overviewItems.ENTITLEMENT_POOLS, count: entitlementPoolsList.length}, + {name: overviewItems.LICENSE_KEY_GROUPS, count: licenseKeyGroupsList.length}, + ]; + return { + vendorName, + licenseModelId: id, + description, + counts, + isReadOnlyMode, + version + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onEditorOpenClick: (name, licenseModelId, version) => { + switch (name) { + case overviewItems.ENTITLEMENT_POOLS: + EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch); + break; + case overviewItems.FEATURE_GROUPS: + FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_AGREEMENTS: + LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_KEY_GROUPS: + LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch); + break; + default: + break; + } + }, + onNavigateClick: ({name, licenseModelId, version}) => { + switch (name) { + case overviewItems.ENTITLEMENT_POOLS: + OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version}); + break; + case overviewItems.FEATURE_GROUPS: + OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_AGREEMENTS: + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_KEY_GROUPS: + OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}); + break; + default: + break; + } + } + }; +}; + +export class SummaryCountList extends React.Component { + + render() { + let {counts} = this.props; + return( + <div className='summary-count-list'> + {counts.map(item => this.renderItem(item))} + </div> + ); + } + + renderItem(item){ + const {name, count} = item; + const {isReadOnlyMode} = this.props; + return( + <SummaryCountItem isReadOnlyMode={isReadOnlyMode} name={name} counter={count} onNavigate={() => this.onNavigate(name)} onAdd={() => this.onAdd(name)} key={name} /> + ); + } + + onAdd(name) { + let {onEditorOpenClick, licenseModelId, isReadOnlyMode, version} = this.props; + if (!isReadOnlyMode) { + onEditorOpenClick(name, licenseModelId, version); + } + } + + onNavigate(name) { + let {onNavigateClick, licenseModelId, version} = this.props; + onNavigateClick({licenseModelId, name, version}); + } +} + +export default connect(mapStateToProps, mapActionsToProps)(SummaryCountList); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js new file mode 100644 index 0000000000..1d65ab9869 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js @@ -0,0 +1,86 @@ +/*! + * 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. + */ +import React from 'react'; +import {connect} from 'react-redux'; + +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import licenseModelOverviewActionHelper from '../licenseModelOverviewActionHelper.js'; +import LicenseModelActionHelper from '../../LicenseModelActionHelper.js'; +import LicenseModelDescriptionEdit from './LicenseModelDescriptionEdit.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {VLM_DESCRIPTION_FORM} from '../LicenseModelOverviewConstants.js'; + +export const mapStateToProps = ({licenseModel: {licenseModelEditor: {data}, licenseModelOverview: {descriptionEditor: {data: descriptionData, genericFieldInfo} }}}) => { + let description = (descriptionData && descriptionData.description) ? descriptionData.description : null; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(data); + return { + data, + description, + genericFieldInfo, + isReadOnlyMode + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: VLM_DESCRIPTION_FORM}), + onCancel: () => licenseModelOverviewActionHelper.editDescriptionClose(dispatch), + onSubmit: (licenseModel) => { + licenseModelOverviewActionHelper.editDescriptionClose(dispatch); + LicenseModelActionHelper.saveLicenseModel(dispatch, {licenseModel}); + }, + onVendorDescriptionEdit: description => licenseModelOverviewActionHelper.editDescriptionOpen(dispatch,{description}) + }; +}; + + + +export class VendorDataView extends React.Component { + render() { + let {data: {vendorName}, description, isReadOnlyMode} = this.props; + return ( + <div className='vendor-data-view'> + <div className='vendor-title'>vendor</div> + <div className='vendor-name' data-test-id='vlm-summary-vendor-name'>{vendorName}</div> + { + description && !isReadOnlyMode ? this.renderDescriptionEdit() : this.renderDescription() + } + </div> + ); + } + + renderDescription() { + let {data: {description}, onVendorDescriptionEdit, isReadOnlyMode} = this.props; + return ( + <div onClick={() => {if (!isReadOnlyMode) {onVendorDescriptionEdit(description);}}} className={!isReadOnlyMode ? 'vendor-description' : 'vendor-description-readonly'}> + <div className='description-data' data-test-id='vlm-summary-vendor-description'> + {description} + </div> + </div> + ); + } + + renderDescriptionEdit() { + let {onCancel, onDataChanged, onSubmit, description, genericFieldInfo, data} = this.props; + return( + <LicenseModelDescriptionEdit onClose={onCancel} onDataChanged={onDataChanged} onSubmit={onSubmit} data={data} genericFieldInfo={genericFieldInfo} description={description}/> + ); + } + +} + +export default connect(mapStateToProps, mapActionsToProps)(VendorDataView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx new file mode 100644 index 0000000000..c63fbff21b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx @@ -0,0 +1,134 @@ +/*! + * 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. + */ +import React from 'react'; +import {catalogItemTypeClasses, migrationStatusMapper} from './onboardingCatalog/OnboardingCatalogConstants.js'; +import CatalogTile from './CatalogTile.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {statusEnum, statusBarTextMap} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import tooltip from './onboardingCatalog/Tooltip.jsx'; + + + +const CatalogTileIcon = ({catalogItemTypeClass}) => ( + <div className={'catalog-tile-icon ' + catalogItemTypeClass}> + <div className='icon'><SVGIcon + name={catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? 'vlm' : 'vsp' }/> + </div> + </div> +); + +const ItemTypeTitle = ({catalogItemTypeClass}) => { + const itemTypeTitle = catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? i18n('VLM') : i18n('VSP'); + return( + <div className={`catalog-tile-type ${catalogItemTypeClass}`}>{itemTypeTitle}</div> + ); +}; + +const CatalogTileVendorName = ({vendorName, catalogItemTypeClass}) => { + const name = catalogItemTypeClass === catalogItemTypeClasses.SOFTWARE_PRODUCT ? vendorName : ''; + return( + <div> + <OverlayTrigger placement='top' overlay={tooltip(name)}> + <div className='catalog-tile-vendor-name'>{name}</div> + </OverlayTrigger> + </div> + ); +}; + +const CatalogTileItemName = ({name}) => ( + <div> + <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> + <div className='catalog-tile-item-name'>{name}</div> + </OverlayTrigger> + </div> +); + +const VersionInfo = ({version}) => ( + <div className='catalog-tile-version-info'> + <div className='catalog-tile-item-version' data-test-id='catalog-item-version'> + V {version} + </div> + </div> +); + +const EntityDetails = ({catalogItemData, catalogItemTypeClass}) => { + const {vendorName, name, version} = catalogItemData; + return ( + <div className='catalog-tile-entity-details'> + <CatalogTileVendorName catalogItemTypeClass={catalogItemTypeClass} vendorName={vendorName}/> + <CatalogTileItemName name={name}/> + <VersionInfo version={version.label} /> + </div> + ); +}; + + +const ItemStatusInfo = ({catalogItemTypeClass, lockingUser, itemStatus}) => { + const status = statusBarTextMap[itemStatus]; + const lockedBy = lockingUser ? ` by ${lockingUser}` : ''; + const toolTipMsg = `${status}${lockedBy}`; + + return ( + <div className={'catalog-tile-content ' + catalogItemTypeClass}> + <div className='catalog-tile-locking-user-name'>{i18n(status)}</div> + <OverlayTrigger placement='top' overlay={tooltip(toolTipMsg)}> + <div className='catalog-tile-check-in-status'><SVGIcon + name={itemStatus === statusEnum.CHECK_OUT_STATUS ? 'unlocked' : 'locked'} + data-test-id={itemStatus === statusEnum.CHECK_IN_STATUS ? 'catalog-item-checked-in' : 'catalog-item-checked-out'}/> + </div> + </OverlayTrigger> + </div> + + ); +}; + +const CatalogItemDetails = ({catalogItemData, catalogItemTypeClass, onSelect, onMigrate}) => { + + let {status: itemStatus} = VersionControllerUtils.getCheckOutStatusKindByUserID(catalogItemData.status, catalogItemData.lockingUser); + + return ( + <CatalogTile catalogItemTypeClass={catalogItemTypeClass} onSelect={() => { + if (catalogItemData.isOldVersion && catalogItemData.isOldVersion === migrationStatusMapper.OLD_VERSION) { + onMigrate({ + softwareProduct: catalogItemData + }); + } else { + onSelect(); + } + }} data-test-id={catalogItemTypeClass}> + <div className='catalog-tile-top item-details'> + <ItemTypeTitle catalogItemTypeClass={catalogItemTypeClass}/> + <CatalogTileIcon catalogItemTypeClass={catalogItemTypeClass}/> + <EntityDetails catalogItemTypeClass={catalogItemTypeClass} catalogItemData={catalogItemData} /> + <ItemStatusInfo itemStatus={itemStatus} catalogItemTypeClass={catalogItemTypeClass} lockingUser={catalogItemData.lockingUser} /> + </div> + </CatalogTile> + ); + +}; + +CatalogItemDetails.PropTypes = { + catalogItemData: React.PropTypes.obj, + catalogItemTypeClass: React.PropTypes.string, + onSelect: React.PropTypes.func, + onMigrate: React.PropTypes.func +}; + +export default CatalogItemDetails; + diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js new file mode 100644 index 0000000000..c4e2724eaf --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js @@ -0,0 +1,36 @@ +import React from 'react'; +import {storiesOf, action} from '@kadira/storybook'; +import {select, withKnobs} from '@kadira/storybook-addon-knobs'; +import CatalogItemDetails from './CatalogItemDetails.jsx'; +import {FinalizedLicenseModelFactory} from 'test-utils/factories/licenseModel/LicenseModelFactories.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + + + +const stories = storiesOf('CatalogTiles', module); +stories.addDecorator(withKnobs); + +const types = [ + 'license-model-type', + 'software-product-type' +]; + +function selectType() { + return select('Item type' , types, types[0]); +} + +let vlm = FinalizedLicenseModelFactory.build({name: 'Test-VLM'}); +let unclockedVlm = {...vlm, status: statusEnum.CHECK_OUT_STATUS}; + + +stories + .add('preview', () => ( + <div className='catalog-view'> + <div className='catalog-list'> + <div className='catalog-items'> + <CatalogItemDetails catalogItemData={vlm} catalogItemTypeClass={selectType()} onSelect={action('onSelect')} onMigrate={action('onMigrate')}/> + <CatalogItemDetails catalogItemData={unclockedVlm} catalogItemTypeClass={selectType()} onSelect={action('onSelect')} onMigrate={action('onMigrate')}/> + </div> + </div> + </div> + )); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx new file mode 100644 index 0000000000..17248e3b02 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx @@ -0,0 +1,51 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; + +const SoftwareProductListHeader = ({selectedVendor, onBack}) => ( + <div className='vendor-page-header'> + <SVGIcon name='back' onClick={onBack}/> + <div className='tab-separator' /> + <div className='vendor-name'>{selectedVendor.vendorName}</div> + </div> +); + +const CatalogList = ({children, onAddVLM, onAddVSP, vendorPageOptions}) => ( + <div className='catalog-list'> + {vendorPageOptions && <SoftwareProductListHeader onBack={vendorPageOptions.onBack} selectedVendor={vendorPageOptions.selectedVendor}/>} + <div className='catalog-items'> + <div className='create-catalog-item-wrapper'> + {onAddVLM && <CreateItemTile onClick={onAddVLM} dataTestId={'catalog-add-new-lm'} className='vlm-type' title={i18n('CREATE NEW VLM')}/>} + {onAddVSP && + <CreateItemTile onClick={onAddVSP} dataTestId={'catalog-add-new-vsp'} className='vsp-type' title={i18n('CREATE NEW VSP')}/>} + </div> + {children} + </div> + </div> +); + +const CreateItemTile = ({onClick, dataTestId, title, className = ''}) => { + return ( + <div className={'create-catalog-item tile ' + className} onClick={() => onClick()} data-test-id={dataTestId}> + <div className='create-item-plus-icon'><SVGIcon name='plus' /></div> + <div className='create-item-text'>{title}</div> + </div> + ); +}; + +export default CatalogList; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogModal.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogModal.jsx new file mode 100644 index 0000000000..1ef9c82822 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogModal.jsx @@ -0,0 +1,62 @@ +/*! + * 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. + */ +import React from 'react'; +import {modalMapper, catalogItemTypes, catalogItemTypeClasses } from './onboardingCatalog/OnboardingCatalogConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import LicenseModelCreation from '../licenseModel/creation/LicenseModelCreation.js'; +import SoftwareProductCreation from '../softwareProduct/creation/SoftwareProductCreation.js'; + +class CatalogModal extends React.Component{ + + 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 {modalToShow} = this.props; + const modalDetails = this.getModalDetails(modalToShow); + + return ( + <Modal + show={Boolean(modalDetails)} + className={`${catalogItemTypeClasses[modalMapper[modalToShow]]}-modal`}> + <Modal.Header> + <Modal.Title>{modalDetails && modalDetails.title}</Modal.Title> + </Modal.Header> + <Modal.Body> + { + modalDetails && modalDetails.element + } + </Modal.Body> + </Modal> + ); + } +} + +export default CatalogModal; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogTile.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogTile.jsx new file mode 100644 index 0000000000..c7720a9d0e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogTile.jsx @@ -0,0 +1,31 @@ +/*! + * 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. + */ +import React from 'react'; + +const CatalogTile = ({catalogItemTypeClass, onSelect, children}) => { + return( + <div className={`catalog-tile tile ${catalogItemTypeClass}`} onClick={(e) => {e.stopPropagation(); e.preventDefault(); onSelect();}} data-test-id={catalogItemTypeClass}> + {children} + </div> + ); +}; + +CatalogTile.PropTypes = { + catalogItemTypeClass: React.PropTypes.string, + onSelect: React.PropTypes.func +}; + +export default CatalogTile; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx new file mode 100644 index 0000000000..ef54848523 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx @@ -0,0 +1,56 @@ +/*! + * 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. + */ +import React from 'react'; +import {catalogItemTypes, modalMapper, catalogItemTypeClasses} from './onboardingCatalog/OnboardingCatalogConstants.js'; +import {filterCatalogItemsByType} from './onboardingCatalog/OnboardingCatalogUtils.js'; +import CatalogList from './CatalogList.jsx'; +import CatalogItemDetails from './CatalogItemDetails.jsx'; + +class DetailsCatalogView extends React.Component{ + + static propTypes = { + VLMList: React.PropTypes.array, + VSPList: React.PropTypes.array, + onSelectVLM: React.PropTypes.func.isRequired, + onSelectVSP: React.PropTypes.func.isRequired, + onAddVLM: React.PropTypes.func.isRequired, + onAddVSP: React.PropTypes.func.isRequired, + filter: React.PropTypes.string.isRequired + }; + + renderCatalogItems(items, type, filter, onSelect, onMigrate, tileType){ + return filterCatalogItemsByType(items, type, filter).map(item => + <CatalogItemDetails + key={item.id} + catalogItemData={type === catalogItemTypes.LICENSE_MODEL ? {...item, name: item.vendorName} : item} + catalogItemTypeClass={catalogItemTypeClasses[modalMapper[type]]} + onMigrate={onMigrate} + onSelect={() => onSelect(item)} + tileType={tileType} /> + ); + } + + render() { + let {VLMList, VSPList, onAddVSP, onAddVLM, onSelectVLM, onSelectVSP, filter = '', onMigrate, tileType} = this.props; + return ( + <CatalogList onAddVLM={onAddVLM} onAddVSP={onAddVSP}> + {this.renderCatalogItems(VLMList, catalogItemTypes.LICENSE_MODEL, filter, onSelectVLM, onMigrate, tileType)} + {this.renderCatalogItems(VSPList, catalogItemTypes.SOFTWARE_PRODUCT, filter, onSelectVSP, onMigrate, tileType)} + </CatalogList> + ); + } +} +export default DetailsCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js b/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js new file mode 100644 index 0000000000..b13ccbbba2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js @@ -0,0 +1,90 @@ +/*! + * 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. + */ + +import {connect} from 'react-redux'; +import OnboardView from './OnboardView.jsx'; +import OnboardingActionHelper from '../OnboardingActionHelper.js'; +import OnboardingCatalogActionHelper from './onboardingCatalog/OnboardingCatalogActionHelper.js'; +import OnboardActionHelper from './OnboardActionHelper.js'; +import LicenseModelCreationActionHelper from '../licenseModel/creation/LicenseModelCreationActionHelper.js'; +import SoftwareProductCreationActionHelper from '../softwareProduct/creation/SoftwareProductCreationActionHelper.js'; +import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; + +export const mapStateToProps = ({ + onboard: {onboardingCatalog, activeTab, searchValue}, licenseModelList, finalizedLicenseModelList, softwareProductList, finalizedSoftwareProductList +}) => { + + const reduceLicenseModelList = (accum, vlm)=> { + let currentSoftwareProductList = sortByStringProperty( + finalizedSoftwareProductList + .filter(vsp => vsp.vendorId === vlm.id), + 'name' + ); + accum.push({...vlm, softwareProductList: currentSoftwareProductList}); + return accum; + }; + + finalizedLicenseModelList = sortByStringProperty( + licenseModelList + .filter(vlm => finalizedLicenseModelList.map(finalVlm => finalVlm.id).includes(vlm.id)) + .reduce(reduceLicenseModelList, []), + 'vendorName' + ); + + finalizedSoftwareProductList = sortByStringProperty( + softwareProductList + .filter(vsp => finalizedSoftwareProductList.map(finalVsp => finalVsp.id).includes(vsp.id)), + 'name' + ); + + + let {activeTab: catalogActiveTab, vendorCatalog: {vspOverlay, selectedVendor}} = onboardingCatalog; + + return { + finalizedLicenseModelList, + finalizedSoftwareProductList, + licenseModelList, + softwareProductList, + activeTab, + catalogActiveTab, + searchValue, + vspOverlay, + selectedVendor + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onSelectLicenseModel({id: licenseModelId, version}) { + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version}); + }, + onSelectSoftwareProduct(softwareProduct) { + let {id: softwareProductId, vendorId: licenseModelId, licensingVersion, version} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version, licenseModelId, licensingVersion}); + }, + onAddSoftwareProductClick: (vendorId) => SoftwareProductCreationActionHelper.open(dispatch, vendorId), + onAddLicenseModelClick: () => LicenseModelCreationActionHelper.open(dispatch), + onVspOverlayChange: (vendor) => OnboardingCatalogActionHelper.changeVspOverlay(dispatch, vendor), + closeVspOverlay: () => OnboardingCatalogActionHelper.closeVspOverlay(dispatch), + onCatalogTabClick: (tab) => OnboardingCatalogActionHelper.changeActiveTab(dispatch, tab), + onTabClick: (tab) => OnboardActionHelper.changeActiveTab(dispatch, tab), + onSearch: (searchValue) => OnboardActionHelper.changeSearchValue(dispatch, searchValue), + onVendorSelect: (vendor) => OnboardingCatalogActionHelper.onVendorSelect(dispatch, {vendor}), + onMigrate: ({softwareProduct}) => OnboardingCatalogActionHelper.onMigrate(dispatch, softwareProduct) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(OnboardView); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js new file mode 100644 index 0000000000..6ebb40878f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js @@ -0,0 +1,45 @@ +/*! + * 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. + */ +import {actionTypes} from './OnboardConstants.js'; + +const OnboardActionHelper = { + resetOnboardStore(dispatch) { + dispatch({ + type: actionTypes.RESET_ONBOARD_STORE + }); + }, + changeActiveTab(dispatch, activeTab) { + this.clearSearchValue(dispatch); + dispatch({ + type: actionTypes.CHANGE_ACTIVE_ONBOARD_TAB, + activeTab + }); + }, + changeSearchValue(dispatch, searchValue) { + dispatch({ + type: actionTypes.CHANGE_SEARCH_VALUE, + searchValue + }); + }, + clearSearchValue(dispatch) { + dispatch({ + type: actionTypes.CHANGE_SEARCH_VALUE, + searchValue: '' + }); + } +}; + +export default OnboardActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardConstants.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardConstants.js new file mode 100644 index 0000000000..41128bfb11 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardConstants.js @@ -0,0 +1,28 @@ +/*! + * 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. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const tabsMapping = { + 'WORKSPACE': 1, + 'CATALOG': 2 +}; + +export const actionTypes = keyMirror({ + CHANGE_ACTIVE_ONBOARD_TAB: null, + CHANGE_SEARCH_VALUE: null, + RESET_ONBOARD_STORE: null, + VSP_MIGRATION: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardReducer.js new file mode 100644 index 0000000000..72145015db --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardReducer.js @@ -0,0 +1,31 @@ +/*! + * 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. + */ +import {actionTypes, tabsMapping} from './OnboardConstants.js'; +import {combineReducers} from 'redux'; +import onboardingCatalogReducer from './onboardingCatalog/OnboardingCatalogReducer.js'; + +const onboardReducer = combineReducers({ + onboardingCatalog: onboardingCatalogReducer, + activeTab: (state = tabsMapping.WORKSPACE, action) => action.type === actionTypes.CHANGE_ACTIVE_ONBOARD_TAB ? action.activeTab : state, + searchValue: (state = '', action) => action.type === actionTypes.CHANGE_SEARCH_VALUE ? action.searchValue : state +}); + +export default (state, action) => { + if (action.type === actionTypes.RESET_ONBOARD_STORE) { + state = undefined; + } + return onboardReducer(state, action); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx new file mode 100644 index 0000000000..b7a7fa5f68 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx @@ -0,0 +1,95 @@ +/*! + * 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. + */ +import React from 'react'; +import OnboardingCatalogView from './onboardingCatalog/OnboardingCatalogView.jsx'; +import WorkspaceView from './workspace/WorkspaceView.jsx'; +import {tabsMapping} from './OnboardConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classnames from 'classnames'; +import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; +import objectValues from 'lodash/values.js'; +import {catalogItemTypes} from './onboardingCatalog/OnboardingCatalogConstants.js'; + +const OnboardHeaderTabs = ({onTabClick, activeTab}) => ( + <div className='onboard-header-tabs'> + <div + className={classnames('onboard-header-tab', {'active': activeTab === tabsMapping.WORKSPACE })} + onClick={() => onTabClick(tabsMapping.WORKSPACE)} + data-test-id='onboard-workspace-tab'> + {i18n('WORKSPACE')} + </div> + <div + className={classnames('onboard-header-tab', {'active': activeTab === tabsMapping.CATALOG })} + onClick={() => onTabClick(tabsMapping.CATALOG)} + data-test-id='onboard-onboard-tab'> + {i18n('ONBOARD CATALOG')} + </div> + </div> +); + +const OnboardHeader = ({onSearch, activeTab, onTabClick, searchValue}) => ( + <div className='onboard-header'> + <OnboardHeaderTabs activeTab={activeTab} onTabClick={onTabClick} /> + <ExpandableInput + onChange={onSearch} + iconType='search' + value={searchValue}/> + </div> +); + +class OnboardView extends React.Component { + static propTypes = { + licenseModelList: React.PropTypes.array, + softwareProductList: React.PropTypes.array, + finalizedLicenseModelList: React.PropTypes.array, + finalizedSoftwareProductList: 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, + closeVspOverlay: React.PropTypes.func.isRequired, + onVspOverlayChange: React.PropTypes.func.isRequired, + onTabClick: React.PropTypes.func.isRequired, + onCatalogTabClick: React.PropTypes.func.isRequired, + onSearch: React.PropTypes.func.isRequired, + activeTab: React.PropTypes.number.isRequired, + catalogActiveTab: React.PropTypes.number.isRequired, + searchValue: React.PropTypes.string.isRequired, + onMigrate: React.PropTypes.func.isRequired, + }; + renderViewByTab(activeTab){ + switch (activeTab){ + case tabsMapping.WORKSPACE: + return <WorkspaceView {...this.props} />; + case tabsMapping.CATALOG: + default: + return <OnboardingCatalogView {...this.props} />; + } + } + + render() { + let {closeVspOverlay, activeTab, onTabClick, onSearch, searchValue} = this.props; + return ( + <div className='catalog-view' onClick={closeVspOverlay}> + <OnboardHeader activeTab={activeTab} onTabClick={onTabClick} searchValue={searchValue} onSearch={value => onSearch(value)}/> + {this.renderViewByTab(activeTab)} + </div> + ); + } +} + +export default OnboardView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js new file mode 100644 index 0000000000..73a447558d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js @@ -0,0 +1,85 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes} from './OnboardingCatalogConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import OnboardActionHelper from '../OnboardActionHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; + + +function getMessageForMigration(name) { + return ( + <div> + <div>{i18n('{name} needs to be updated. Click ‘Checkout & Update’, to proceed.',{name})}</div> + <div>{i18n('Please don’t forget to submit afterwards')}</div> + </div> + ); +} + +const OnboardingCatalogActionHelper = { + changeVspOverlay(dispatch, vendor) { + dispatch({ + type: actionTypes.CHANGE_VSP_OVERLAY, + vendorId: vendor ? vendor.id : null + }); + }, + closeVspOverlay(dispatch) { + dispatch({ + type: actionTypes.CLOSE_VSP_OVERLAY + }); + }, + changeActiveTab(dispatch, activeTab) { + OnboardActionHelper.clearSearchValue(dispatch); + dispatch({ + type: actionTypes.CHANGE_ACTIVE_CATALOG_TAB, + activeTab + }); + }, + onVendorSelect(dispatch, {vendor}) { + OnboardActionHelper.clearSearchValue(dispatch); + dispatch({ + type: actionTypes.ONBOARDING_CATALOG_OPEN_VENDOR_PAGE, + selectedVendor: vendor + }); + }, + onMigrate(dispatch, softwareProduct) { + const {status, name, lockingUser} = softwareProduct; + if (status === statusEnum.CHECK_OUT_STATUS && !VersionControllerUtils.isCheckedOutByCurrentUser(softwareProduct)) { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data: { + title: 'WARNING', + msg: i18n('{name} is locked by user {lockingUser} for self-healing',{name, lockingUser}) + } + }); + } else { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + title: 'WARNING', + msg: getMessageForMigration(softwareProduct.name.toUpperCase()), + confirmationButtonText: i18n('Checkout & Update'), + onConfirmed: ()=>SoftwareProductActionHelper.migrateSoftwareProduct(dispatch, {softwareProduct}) + } + }); + } + } +}; + +export default OnboardingCatalogActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js new file mode 100644 index 0000000000..071160c4fd --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js @@ -0,0 +1,50 @@ +/*! + * 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. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const catalogItemTypes = Object.freeze({ + LICENSE_MODEL: 'license-model', + SOFTWARE_PRODUCT: 'software-product' +}); + +export const catalogItemTypeClasses = { + LICENSE_MODEL: 'license-model-type', + SOFTWARE_PRODUCT: 'software-product-type', + VENDOR: 'vendor-type' + +}; + +export const modalMapper = { + 'license-model': 'LICENSE_MODEL', + 'software-product': 'SOFTWARE_PRODUCT' +}; + +export const tabsMapping = { + 'BY_VENDOR': 1, + 'ALL': 2 +}; + +export const migrationStatusMapper = { + OLD_VERSION: 'True', +}; + +export const actionTypes = keyMirror({ + ONBOARDING_CATALOG_OPEN_VENDOR_PAGE: null, + CHANGE_ACTIVE_CATALOG_TAB: null, + CHANGE_SEARCH_VALUE: null, + CHANGE_VSP_OVERLAY: null, + CLOSE_VSP_OVERLAY: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogReducer.js new file mode 100644 index 0000000000..d7d9d0bd6c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogReducer.js @@ -0,0 +1,30 @@ +/*! + * 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. + */ +import {actionTypes, tabsMapping} from './OnboardingCatalogConstants.js'; +import {combineReducers} from 'redux'; +import vendorCatalogReducer from './VendorCatalogReducer.js'; + +const onboardingCatalogReducer = combineReducers({ + vendorCatalog: vendorCatalogReducer, + activeTab: (state = tabsMapping.ALL, action) => action.type === actionTypes.CHANGE_ACTIVE_CATALOG_TAB ? action.activeTab : state +}); + +export default (state, action) => { + if (action.type === actionTypes.RESET_ONBOARDING_CATALOG_STORE) { + state = undefined; + } + return onboardingCatalogReducer(state, action); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogUtils.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogUtils.js new file mode 100644 index 0000000000..ac623db920 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogUtils.js @@ -0,0 +1,21 @@ +/*! + * 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. + */ +import {catalogItemTypes} from './OnboardingCatalogConstants.js'; + +export const filterCatalogItemsByType = (items, type, filter) => { + const fieldName = type === catalogItemTypes.LICENSE_MODEL ? 'vendorName' : 'name'; + return items.filter(item => item[fieldName].toLowerCase().indexOf(filter.toLowerCase()) > -1); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx new file mode 100644 index 0000000000..b1f002d2fb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx @@ -0,0 +1,98 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classnames from 'classnames'; +import DetailsCatalogView from 'sdc-app/onboarding/onboard/DetailsCatalogView.jsx'; +import VendorCatalogView from './VendorCatalogView.jsx'; +import { tabsMapping} from './OnboardingCatalogConstants.js'; + +const CatalogHeaderTabs = ({onTabPress, activeTab}) => ( + <div className='catalog-header-tabs'> + <div + className={classnames('catalog-header-tab', {'active': activeTab === tabsMapping.ALL })} + onClick={() => onTabPress(tabsMapping.ALL)} + data-test-id='catalog-all-tab'> + {i18n('ALL')} + </div> + <div className='tab-separator'/> + <div + className={classnames('catalog-header-tab', {'active': activeTab === tabsMapping.BY_VENDOR })} + onClick={() => onTabPress(tabsMapping.BY_VENDOR)} + data-test-id='catalog-by-vendor-tab'> + {i18n('BY VENDOR')} + </div> + </div> +); + +const CatalogHeader = ({activeTab, onTabPress}) => ( + <div className='catalog-header'> + <CatalogHeaderTabs activeTab={activeTab} onTabPress={onTabPress} /> + </div> +); + +class OnboardingCatalogView extends React.Component { + renderViewByTab(activeTab){ + const {finalizedLicenseModelList: licenseModelList, vspOverlay, finalizedSoftwareProductList: softwareProductList, onSelectLicenseModel, onSelectSoftwareProduct, + onAddLicenseModelClick, onAddSoftwareProductClick, onVspOverlayChange, onVendorSelect, selectedVendor, searchValue, onMigrate} = this.props; + switch (activeTab){ + case tabsMapping.ALL: + return ( + <DetailsCatalogView + VLMList={licenseModelList} + VSPList={softwareProductList} + onAddVLM={onAddLicenseModelClick} + onAddVSP={onAddSoftwareProductClick} + onSelectVLM={onSelectLicenseModel} + onSelectVSP={onSelectSoftwareProduct} + filter={searchValue} + onMigrate={onMigrate}/> + ); + case tabsMapping.BY_VENDOR: + default: + return ( + <VendorCatalogView + licenseModelList={licenseModelList} + onAddVSP={onAddSoftwareProductClick} + onAddVLM={onAddLicenseModelClick} + onSelectVSP={onSelectSoftwareProduct} + onSelectVLM={onSelectLicenseModel} + vspOverlay={vspOverlay} + onVendorSelect={onVendorSelect} + selectedVendor={selectedVendor} + onVspOverlayChange={onVspOverlayChange} + onMigrate={onMigrate} + filter={searchValue}/> + ); + } + } + + render() { + const {selectedVendor, catalogActiveTab: activeTab, onCatalogTabClick, onSearch, searchValue} = this.props; + return ( + <div className='catalog-wrapper'> + {!selectedVendor && <CatalogHeader + onSearch={event => onSearch(event.target.value)} + activeTab={activeTab} + onTabPress={tab => onCatalogTabClick(tab)} + searchValue={searchValue}/>} + {this.renderViewByTab(activeTab)} + </div> + ); + } +} + +export default OnboardingCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/Tooltip.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/Tooltip.jsx new file mode 100644 index 0000000000..8d8d1162a0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/Tooltip.jsx @@ -0,0 +1,24 @@ +/*! + * 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. + */ + +import React from 'react'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +export default function tooltip (msg) { + return ( + <Tooltip className='tile-tooltip' id='tile-tooltip'>{msg}</Tooltip> + ); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VSPOverlay.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VSPOverlay.jsx new file mode 100644 index 0000000000..1ba4834fa3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VSPOverlay.jsx @@ -0,0 +1,50 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {migrationStatusMapper} from './OnboardingCatalogConstants.js'; + +const VSPOverlay = ({VSPList, onSelectVSP, onSeeMore, onMigrate}) => ( + <div className='vsp-overlay-wrapper' onClick={(e) => { + e.stopPropagation(); + e.preventDefault(); + }}> + <div className='vsp-overlay-arrow'></div> + <div className='vsp-overlay'> + <div className='vsp-overlay-title'>{i18n('Recently Edited')}</div> + <div className='vsp-overlay-list'> + {VSPList.slice(0, 5).map(vsp => <div key={vsp.id} className='vsp-overlay-detail' onClick={() => { + if (vsp.isOldVersion && vsp.isOldVersion === migrationStatusMapper.OLD_VERSION) { + onMigrate({ + softwareProduct: vsp + }); + } else { + onSelectVSP(vsp); + } + } + }>{i18n(vsp.name)}</div>)} + </div> + {VSPList.length > 5 && <div className='vsp-overlay-see-more' onClick={onSeeMore}>{i18n('See More')}</div>} + </div> + </div> +); + +VSPOverlay.PropTypes = { + VSPList: React.PropTypes.array, + onSelectVSP: React.PropTypes.func +}; + +export default VSPOverlay; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogReducer.js new file mode 100644 index 0000000000..dd8b41bd22 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogReducer.js @@ -0,0 +1,38 @@ +/*! + * 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. + */ +import {actionTypes} from './OnboardingCatalogConstants.js'; + +export default (state = {}, action) => { + switch(action.type) { + case actionTypes.ONBOARDING_CATALOG_OPEN_VENDOR_PAGE: + return { + ...state, + selectedVendor: action.selectedVendor + }; + case actionTypes.CHANGE_VSP_OVERLAY: + return { + ...state, + vspOverlay: action.vendorId + }; + case actionTypes.CLOSE_VSP_OVERLAY: + return { + ...state, + vspOverlay: null + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogView.jsx new file mode 100644 index 0000000000..c4e0599d85 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogView.jsx @@ -0,0 +1,74 @@ +/*! + * 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. + */ +import React from 'react'; +import VendorItem from './VendorItem.jsx'; +import CatalogList from '../CatalogList.jsx'; +import CatalogItemDetails from '../CatalogItemDetails.jsx'; +import {catalogItemTypes, catalogItemTypeClasses} from './OnboardingCatalogConstants.js'; +import {filterCatalogItemsByType} from './OnboardingCatalogUtils.js'; + +const VendorList = ({onAddVLM, onAddVSP, onSelectVSP, licenseModelList = [], vspOverlay: currentOverlay, onVspOverlayChange, onVendorSelect, filter, onMigrate}) => { + return( + <CatalogList onAddVLM={onAddVLM} onAddVSP={onAddVSP}> + { + filterCatalogItemsByType(licenseModelList, catalogItemTypes.LICENSE_MODEL, filter).map(vlm => + <VendorItem + key={vlm.id} + onAddVSP={onAddVSP} + onSelectVSP={onSelectVSP} + shouldShowOverlay={currentOverlay === vlm.id} + onVSPIconClick={(hasVSP) => onVspOverlayChange(vlm.id === currentOverlay || !hasVSP ? null : vlm)} + onVendorSelect={onVendorSelect} + onMigrate={onMigrate} + vendor={vlm}/>) + } + </CatalogList> + ); +}; + +const SoftwareProductListByVendor = ({onAddVSP, selectedVendor, onVendorSelect, onSelectVSP, onSelectVLM, filter, onMigrate}) => { + return( + <div> + <CatalogList onAddVSP={()=>{onAddVSP(selectedVendor.id);}} vendorPageOptions={{selectedVendor, onBack: () => onVendorSelect(false)}}> + <CatalogItemDetails + key={selectedVendor.id} + onSelect={() => onSelectVLM(selectedVendor)} + catalogItemTypeClass={catalogItemTypeClasses.LICENSE_MODEL} + onMigrate={onMigrate} + catalogItemData={{...selectedVendor, name: selectedVendor.vendorName}}/> + { + filterCatalogItemsByType(selectedVendor.softwareProductList, catalogItemTypes.SOFTWARE_PRODUCT, filter).map(vsp => + <CatalogItemDetails + key={vsp.id} + catalogItemTypeClass={catalogItemTypeClasses.SOFTWARE_PRODUCT} + onMigrate={onMigrate} + onSelect={() => onSelectVSP(vsp)} + catalogItemData={vsp}/> + ) + } + </CatalogList> + </div> + ); +}; + +class VendorCatalogView extends React.Component { + render() { + let {selectedVendor} = this.props; + return( selectedVendor ? <SoftwareProductListByVendor {...this.props}/> : <VendorList {...this.props}/>); + } +} + +export default VendorCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx new file mode 100644 index 0000000000..cecccdd9ad --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx @@ -0,0 +1,96 @@ +/*! + * 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. + */ +import React from 'react'; +import {catalogItemTypeClasses} from './OnboardingCatalogConstants.js'; +import CatalogTile from '../CatalogTile.jsx'; +import classnames from 'classnames'; +import VSPOverlay from './VSPOverlay.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import tooltip from './Tooltip.jsx'; + + +class VendorItem extends React.Component { + + static PropTypes = { + softwareProductList: React.PropTypes.array, + vendor: React.PropTypes.object, + onSelectVSP: React.PropTypes.func, + shouldShowOverlay: React.PropTypes.boolm, + onVendorSelect: React.PropTypes.func, + onAddVSP: React.PropTypes.func, + onVSPIconClick: React.PropTypes.func, + + }; + + render() { + let {vendor, onSelectVSP, shouldShowOverlay, onVendorSelect, onMigrate} = this.props; + let {softwareProductList = [], vendorName} = vendor; + return ( + <CatalogTile + catalogItemTypeClass={catalogItemTypeClasses.VENDOR} + onSelect={() => onVendorSelect(vendor)}> + <div className='catalog-tile-top'> + <div className='catalog-tile-icon vendor-type'> + <div className='icon'><SVGIcon name='vendor'/></div> + </div> + <OverlayTrigger placement='top' overlay={tooltip(vendorName)}> + <div className='catalog-tile-item-name'>{vendorName}</div> + </OverlayTrigger> + <div + className={classnames('catalog-tile-vsp-count', {active: shouldShowOverlay}, {clickable: softwareProductList.length})} + onClick={(event) => this.handleVspCountClick(event)} + data-test-id='catalog-vsp-count'> + {i18n(`${softwareProductList.length} VSPs`)} + </div> + <div className='catalog-tile-content' onClick={(event) => this.onCreateVspClick(event)} data-test-id='catalog-create-new-vsp-from-vendor'> + <div className='create-new-vsp-button'> + <SVGIcon name='plus'/> {i18n('Create new VSP')} + </div> + </div> + </div> + + {shouldShowOverlay && softwareProductList.length > 0 + && <VSPOverlay onMigrate={onMigrate} VSPList={softwareProductList} onSelectVSP={onSelectVSP} onSeeMore={() => onVendorSelect(vendor)}/>} + </CatalogTile> + ); + } + + onClick(vlm) { + this.setState({ + licenseModelToShow: vlm + }); + } + + onCreateVspClick(event) { + let {onAddVSP, vendor: {id}} = this.props; + event.stopPropagation(); + event.preventDefault(); + onAddVSP(id); + } + + handleVspCountClick(e){ + let {onVSPIconClick, vendor: {softwareProductList}} = this.props; + e.stopPropagation(); + e.preventDefault(); + const hasVSP = Boolean(softwareProductList.length); + onVSPIconClick(hasVSP); + } + +} + +export default VendorItem; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceView.jsx new file mode 100644 index 0000000000..d86b674f13 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceView.jsx @@ -0,0 +1,57 @@ +/*! + * 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. + */ +import React from 'react'; +import DetailsCatalogView from '../DetailsCatalogView.jsx'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {tabsMapping} from 'sdc-app/onboarding/onboard/OnboardConstants.js'; + +const WorkspaceView = (props) => { + let { + licenseModelList, softwareProductList, onAddLicenseModelClick, + onAddSoftwareProductClick, onSelectLicenseModel, onSelectSoftwareProduct, searchValue, onMigrate + } = props; + + let {getCheckOutStatusKindByUserID} = VersionControllerUtils; + let unfinalizedLicenseModelList = licenseModelList.filter(vlm => { + let {status} = getCheckOutStatusKindByUserID(vlm.status, vlm.lockingUser); + return status !== statusEnum.SUBMIT_STATUS && status !== statusEnum.LOCK_STATUS; + }); + let unfinalizedSoftwareProductList = softwareProductList.filter(vsp =>{ + let {status} = getCheckOutStatusKindByUserID(vsp.status, vsp.lockingUser); + return status !== statusEnum.SUBMIT_STATUS && status !== statusEnum.LOCK_STATUS; + }); + + return ( + <div className='catalog-wrapper workspace-view'> + <div className='catalog-header workspace-header'> + {i18n('WORKSPACE')} + </div> + <DetailsCatalogView + VLMList={unfinalizedLicenseModelList} + VSPList={unfinalizedSoftwareProductList} + onAddVLM={onAddLicenseModelClick} + onAddVSP={onAddSoftwareProductClick} + onSelectVLM={onSelectLicenseModel} + onSelectVSP={onSelectSoftwareProduct} + onMigrate={onMigrate} + filter={searchValue} + tileType={tabsMapping.WORKSPACE} /> + </div>); +}; + +export default WorkspaceView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/FinalizedSoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/FinalizedSoftwareProductReducer.js new file mode 100644 index 0000000000..396f65f5d7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/FinalizedSoftwareProductReducer.js @@ -0,0 +1,25 @@ +/*! + * 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. + */ +import {actionTypes} from './SoftwareProductConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED: + return [...action.response.results]; + 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 index 2dbef6baf2..12f68a2afe 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -1,36 +1,40 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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 {navigationItems, mapScreenToNavigationItem} from './SoftwareProductConstants.js'; import SoftwareProductActionHelper from './SoftwareProductActionHelper.js'; import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductDependenciesActionHelper from './dependencies/SoftwareProductDependenciesActionHelper.js'; +import {doesHeatDataExist} from './attachments/SoftwareProductAttachmentsUtils.js'; + +import HeatSetupActionHelper from './attachments/setup/HeatSetupActionHelper.js'; +import { actionsEnum as versionControllerActions } from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +function getActiveNavigationId(screen, componentId) { + let activeItemId = componentId ? mapScreenToNavigationItem[screen] + '|' + componentId : mapScreenToNavigationItem[screen]; + return activeItemId; +} const buildComponentNavigationBarGroups = ({componentId, meta}) => { const groups = ([ @@ -106,6 +110,18 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co id: navigationItems.ATTACHMENTS, name: i18n('Attachments'), disabled: false, + hidden: !doesHeatDataExist(meta.heatSetup), + meta + }, { + id: navigationItems.ACTIVITY_LOG, + name: i18n('Activity Log'), + disabled: false, + meta + }, { + id: navigationItems.DEPENDENCIES, + name: i18n('Component Dependencies'), + hidden: componentsList.length <= 1, + disabled: false, meta }, { id: navigationItems.COMPONENTS, @@ -125,29 +141,7 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co } ] }]; - 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; - } - + let activeItemId = getActiveNavigationId(screen, componentId); return { activeItemId, groups }; @@ -158,9 +152,7 @@ const buildVersionControllerProps = (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}; + const {status, isCheckedOut} = VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser); return { status, isCheckedOut, version, viewableVersions, @@ -168,42 +160,56 @@ const buildVersionControllerProps = (softwareProduct) => { }; }; -const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { - const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire} = softwareProduct; - const {data: currentSoftwareProduct = {}, mapOfExpandedIds = []} = softwareProductEditor; +function buildMeta({softwareProduct, componentId, softwareProductDependencies}) { + const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire, softwareProductAttachments} = softwareProduct; + const {data: currentSoftwareProduct = {}} = softwareProductEditor; const {version} = currentSoftwareProduct; - const {componentsList = []} = softwareProductComponents; const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); const {qdata} = softwareProductQuestionnaire; + const {heatSetup, heatSetupCache} = softwareProductAttachments; let currentComponentMeta = {}; if(componentId) { const {componentEditor: {data: componentData = {} , qdata: componentQdata}} = softwareProductComponents; currentComponentMeta = {componentData, componentQdata}; } - const meta = {softwareProduct: currentSoftwareProduct, qdata, version, isReadOnlyMode, currentComponentMeta}; + const meta = {softwareProduct: currentSoftwareProduct, qdata, version, heatSetup, heatSetupCache, isReadOnlyMode, currentComponentMeta, softwareProductDependencies}; + return meta; +} + +const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { + const {softwareProductEditor, softwareProductComponents, softwareProductDependencies} = softwareProduct; + const {mapOfExpandedIds = []} = softwareProductEditor; + const {componentsList = []} = softwareProductComponents; + const meta = buildMeta({softwareProduct, componentId, softwareProductDependencies}); return { versionControllerProps: buildVersionControllerProps(softwareProduct), - navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}), + meta }; }; -const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, meta: {isReadOnlyMode, softwareProduct, qdata, currentComponentMeta: {componentData, componentQdata}}}) => { +const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, + meta: {isReadOnlyMode, softwareProduct, version, qdata, softwareProductDependencies, + currentComponentMeta: {componentData, componentQdata}}}) => { let promise; if (isReadOnlyMode) { promise = Promise.resolve(); } else { switch(screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + promise = SoftwareProductDependenciesActionHelper.saveDependencies(dispatch,{softwareProductId, version, dependenciesList: softwareProductDependencies}); 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}); + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + {softwareProductId, version, 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}); + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata: componentQdata}); break; default: promise = Promise.resolve(); @@ -228,22 +234,22 @@ const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentC OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.COMPUTE: - OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.LOAD_BALANCING: - OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.NETWORKS: OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.STORAGE: - OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.PROCESS_DETAILS: - OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.MONITORING: - OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; } }; @@ -251,44 +257,49 @@ const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentC 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}); + onVersionSwitching: (version, meta) => { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); + props.onNavigate({id: getActiveNavigationId(screen, currentComponentId), meta, 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(() => { + onNavigate: ({id, meta, version}) => { + let {heatSetup, heatSetupCache} = meta; + let heatSetupPopupPromise = screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS ? + HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : + Promise.resolve(); + let preNavigate = meta ? autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}) : Promise.resolve(); + version = version || (meta ? meta.version : undefined); + Promise.all([preNavigate, heatSetupPopupPromise]).then(() => { switch(id) { case navigationItems.VENDOR_SOFTWARE_PRODUCT: - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: meta.version}); + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); break; case navigationItems.GENERAL: - OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId, version}); break; case navigationItems.PROCESS_DETAILS: - OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: meta.version}); + OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}); break; case navigationItems.NETWORKS: - OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: meta.version}); + OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version}); + break; + case navigationItems.DEPENDENCIES: + OnboardingActionHelper.navigateToSoftwareProductDependencies(dispatch, {softwareProductId, version}); break; case navigationItems.ATTACHMENTS: - OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}); break; case navigationItems.COMPONENTS: - OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId, version}); + break; + case navigationItems.ACTIVITY_LOG: + OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version}); break; default: - onComponentNavigate(dispatch, {id, softwareProductId, version: meta.version, screen, currentComponentId}); + onComponentNavigate(dispatch, {id, softwareProductId, version, screen, currentComponentId}); break; } - }); + }).catch(() => {}); } }; @@ -297,6 +308,8 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: @@ -310,11 +323,20 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr } - props.onVersionControllerAction = (action) => - SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action}).then(() => { - SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); - }); - + props.onVersionControllerAction = (action, version, meta) => { + let {heatSetup, heatSetupCache} = meta; + let heatSetupPopupPromise = screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS && action === versionControllerActions.CHECK_IN ? + HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : + Promise.resolve(); + heatSetupPopupPromise.then(() => { + return SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action, version}).then(({newVersion}) => { + //props.onNavigate({id: getActiveNavigationId(screen, currentComponentId), version}); + if(screen === enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG) { + OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version: newVersion}); + } + }); + }).catch(() => {}); + }; return props; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js index d9ed8af679..6f53886350 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -26,10 +21,17 @@ import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licens 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'; +import {actionTypes as HeatSetupActions} from 'sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js'; +import {actionTypes as featureGroupsActionConstants} from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js'; +import {actionTypes as licenseAgreementActionTypes} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -40,46 +42,58 @@ function softwareProductCategoriesUrl() { return `${restATTPrefix}/v1/categories/resources/`; } -function uploadFile(vspId, formData) { - - return RestAPIUtil.create(`${baseUrl()}${vspId}/upload`, formData); +function uploadFile(vspId, formData, version) { + return RestAPIUtil.post(`${baseUrl()}${vspId}/versions/${version.id}/orchestration-template-candidate`, 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 putSoftwareProduct(softwareProduct) { + return RestAPIUtil.put(`${baseUrl()}${softwareProduct.id}/versions/${softwareProduct.version.id}`, { + name: softwareProduct.name, + description: softwareProduct.description, + category: softwareProduct.category, + subCategory: softwareProduct.subCategory, + vendorId: softwareProduct.vendorId, + vendorName: softwareProduct.vendorName, + licensingVersion: softwareProduct.licensingVersion && softwareProduct.licensingVersion.id ? softwareProduct.licensingVersion : {} , + icon: softwareProduct.icon, + licensingData: softwareProduct.licensingData }); } -function putSoftwareProductQuestionnaire(vspId, qdata) { - return RestAPIUtil.save(`${baseUrl()}${vspId}/questionnaire`, qdata); +function putSoftwareProductQuestionnaire(vspId, qdata, version) { + return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata); } -function putSoftwareProductAction(id, action) { - return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +function putSoftwareProductAction(id, action, version) { + return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action}); } function fetchSoftwareProductList() { return RestAPIUtil.fetch(baseUrl()); } +function fetchFinalizedSoftwareProductList() { + return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`); +} + function fetchSoftwareProduct(vspId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl()}${vspId}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`); } function fetchSoftwareProductQuestionnaire(vspId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl()}${vspId}/questionnaire${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`); +} + +function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) { + return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate); +} +function validateHeatCandidate(softwareProductId, version) { + return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`); +} + +function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) { + return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'}); } function objToString(obj) { @@ -88,7 +102,8 @@ function objToString(obj) { obj.forEach((item) => { str += objToString(item) + '\n'; }); - } else { + } + else { for (let p in obj) { if (obj.hasOwnProperty(p)) { str += obj[p] + '\n'; @@ -115,24 +130,27 @@ function fetchSoftwareProductCategories(dispatch) { }); return RestAPIUtil.fetch(softwareProductCategoriesUrl()) .then(handleResponse) - .fail(() => handleResponse(null)); + .catch(() => handleResponse(null)); } function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) { - LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}); - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion}); + return Promise.all([ + 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) { + for (let i = 0; i < items.length; i++) { + if (items[i].id === itemIdToToggle) { if (items[i].expanded) { return {}; - } else { + } + else { return {[itemIdToToggle]: true}; } } - else if(items[i].items && items[i].items.length > 0) { + 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; @@ -143,8 +161,63 @@ function getExpandedItemsId(items, itemIdToToggle) { return false; } +function getTimestampString() { + let date = new Date(); + let z = n => n < 10 ? '0' + n : n; + return `${date.getFullYear()}-${z(date.getMonth())}-${z(date.getDate())}_${z(date.getHours())}-${z(date.getMinutes())}`; +} + +function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) { + let filename; + let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : ''; + let match = contentDisposition.match(/filename=(.*?)(;|$)/); + if (match) { + filename = match[1]; + } + else { + filename = defaultFilename; + } + + if (addTimestamp) { + filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`); + } + + let link = document.createElement('a'); + let url = URL.createObjectURL(blob); + link.href = url; + link.download = filename; + link.style.display = 'none'; + document.body.appendChild(link); + link.click(); + setTimeout(function(){ + document.body.removeChild(link); + URL.revokeObjectURL(url); + }, 0); +} + +function migrateSoftwareProduct(vspId, version) { + return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`); +} + +function adjustMinorVersion(version, value) { + let ar = version.split('.'); + return ar[0] + '.' + (parseInt(ar[1]) + value); +} + +function adjustMajorVersion(version, value) { + let ar = version.split('.'); + return (parseInt(ar[0]) + value) + '.0'; +} + const SoftwareProductActionHelper = { + fetchFinalizedSoftwareProductList(dispatch) { + return fetchFinalizedSoftwareProductList().then(response => dispatch({ + type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED, + response + })); + }, + loadSoftwareProductAssociatedData(dispatch) { fetchSoftwareProductCategories(dispatch); LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); @@ -152,7 +225,7 @@ const SoftwareProductActionHelper = { loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) { SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); - loadLicensingData(dispatch, {licenseModelId, licensingVersion}); + return loadLicensingData(dispatch, {licenseModelId, licensingVersion}); }, fetchSoftwareProductList(dispatch) { @@ -162,37 +235,61 @@ const SoftwareProductActionHelper = { })); }, - uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle}) { + loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}){ + return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`).then(response => dispatch({ + type: HeatSetupActions.MANIFEST_LOADED, + response + })); + }, + + updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){ + return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version); + }, + + processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){ + return validateHeatCandidate(softwareProductId, version).then(response => { + if (response.status === 'Success') { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); + } + }); + }, + + uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) { + dispatch({ + type: HeatSetupActions.FILL_HEAT_SETUP_CACHE, + payload: {} + }); + Promise.resolve() - .then(() => uploadFile(softwareProductId, formData)) + .then(() => uploadFile(softwareProductId, formData, version)) .then(response => { - if (response.status !== 'Success') { + if (response.status === 'Success') { + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}); + } + else { 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} + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + title: failedNotificationTitle, + msg: error.message + } }); }); }, - uploadConfirmation(dispatch, {softwareProductId, formData, failedNotificationTitle}) { - dispatch({ - type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION, - uploadData: { - softwareProductId, - formData, - failedNotificationTitle - } - }); + downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){ + let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}); + p.then(() => { + fetchOrchestrationTemplateCandidate(softwareProductId, version) + .then((blob, statusText, xhr) => showFileSaveDialog({blob, xhr, defaultFilename: 'HEAT_file.zip', addTimestamp: true})); + }, null/* do not download if data was not saved correctly*/); }, + hideUploadConfirm (dispatch) { dispatch({ type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION @@ -208,7 +305,8 @@ const SoftwareProductActionHelper = { ), SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, { softwareProductId: softwareProduct.id, - qdata + qdata, + version: softwareProduct.version }) ]); }, @@ -217,8 +315,8 @@ const SoftwareProductActionHelper = { return putSoftwareProduct(softwareProduct); }, - updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata}) { - return putSoftwareProductQuestionnaire(softwareProductId, qdata); + updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) { + return putSoftwareProductQuestionnaire(softwareProductId, qdata, version); }, softwareProductEditorDataChanged(dispatch, {deltaData}) { @@ -235,10 +333,34 @@ const SoftwareProductActionHelper = { }); }, - 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}); + softwareProductEditorVendorChanged(dispatch, {deltaData, formName}) { + if (deltaData.licensingVersion.id){ + let p = Promise.all([ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, { + licenseModelId: deltaData.vendorId, + version: {id: deltaData.licensingVersion.id} + }), + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, { + licenseModelId: deltaData.vendorId, + version: {id: deltaData.licensingVersion.id} + }) + ]); + ValidationHelper.dataChanged(dispatch, {deltaData, formName}); + return p; + } else { + ValidationHelper.dataChanged(dispatch, {deltaData, formName}); + + dispatch({ + type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED, + response: {results: []} + }); + + dispatch({ + type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED, + response: {results: []} + }); + } + }, setIsValidityData(dispatch, {isValidityData}) { @@ -265,55 +387,67 @@ const SoftwareProductActionHelper = { 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) - } - }); + ValidationHelper.qDataLoaded(dispatch, {response: {qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema)}, qName: PRODUCT_QUESTIONNAIRE}); }) ]); }, - performVCAction(dispatch, {softwareProductId, action}) { + performVCAction(dispatch, {softwareProductId, action, version}) { if (action === VersionControllerActionsEnum.SUBMIT) { - return putSoftwareProductAction(softwareProductId, action).then(() => { - return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE).then(() => { + return putSoftwareProductAction(softwareProductId, action, version).then(() => { + return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE, version).then(() => { dispatch({ - type: NotificationConstants.NOTIFY_SUCCESS, + type: modalActionTypes.GLOBAL_MODAL_SUCCESS, data: { title: i18n('Submit Succeeded'), msg: i18n('This software product successfully submitted'), + cancelButtonText: i18n('OK'), timeout: 2000 } }); - fetchSoftwareProduct(softwareProductId).then(response => { - dispatch({ - type: actionTypes.SOFTWARE_PRODUCT_LOADED, - response - }); - }); + const newVersionId = adjustMajorVersion(version.label, 1); + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}}); + return Promise.resolve({newVersion: {id: newVersionId}}); }); }, error => dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {title: i18n('Submit Failed'), validationResponse: error.responseJSON} + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE, + title: i18n('Submit Failed'), + modalComponentProps: { + validationResponse: error.responseJSON + }, + cancelButtonText: i18n('Ok') + } })); } else { - return putSoftwareProductAction(softwareProductId, action).then(() => { - fetchSoftwareProduct(softwareProductId).then(response => { - dispatch({ - type: actionTypes.SOFTWARE_PRODUCT_LOADED, - response - }); - }); + return putSoftwareProductAction(softwareProductId, action, version).then(() => { + let newVersionId = version.id; + /* + TODO Temorary switch to change version label + */ + switch(action) { + case VersionControllerActionsEnum.CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, 1); + break; + case VersionControllerActionsEnum.UNDO_CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, -1); + break; + } + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}}); + return Promise.resolve({newVersion: {id: newVersionId}}); }); } }, switchVersion(dispatch, {softwareProductId, licenseModelId, version}) { - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version}); + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, { + softwareProductId, + licenseModelId, + version + }); }, toggleNavigationItems(dispatch, {items, itemIdToExpand}) { @@ -327,7 +461,17 @@ const SoftwareProductActionHelper = { /** for the next verision */ addComponent(dispatch) { return dispatch; + }, + + migrateSoftwareProduct(dispatch, {softwareProduct}) { + let {licenseModelId, licensingVersion, id: softwareProductId, version, status} = softwareProduct; + const newVer = status === statusEnum.CHECK_IN_STATUS || status === statusEnum.SUBMIT_STATUS ? + adjustMinorVersion(version.id, 1) : version.id; + migrateSoftwareProduct(softwareProductId, version) + .then(() =>OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, + {softwareProductId, version: {id: newVer, label: newVer}, licenseModelId, licensingVersion})); } + }; 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 index 812afe5409..9b147415f9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - export default { getCurrentCategoryOfSubCategory(selectedSubCategory, softwareProductCategories) { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js index 5f10c27084..f29b0f6e0d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js @@ -1,28 +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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; +import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; export const actionTypes = keyMirror({ SOFTWARE_PRODUCT_LOADED: null, SOFTWARE_PRODUCT_LIST_LOADED: null, + FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED: null, SOFTWARE_PRODUCT_LIST_EDIT: null, SOFTWARE_PRODUCT_CATEGORIES_LOADED: null, SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE: null, @@ -33,21 +30,46 @@ export const actionTypes = keyMirror({ OPEN: null, CLOSE: null, DATA_CHANGED: null, - IS_VALIDITY_DATA_CHANGED: null, - UPLOAD_CONFIRMATION: null + IS_VALIDITY_DATA_CHANGED: null } }); export const navigationItems = keyMirror({ - VENDOR_SOFTWARE_PRODUCT: 'Vendor Software Product', - GENERAL: 'General', - PROCESS_DETAILS: 'Process Details', - NETWORKS: 'Networks', - ATTACHMENTS: 'Attachments', - COMPONENTS: 'Components', + VENDOR_SOFTWARE_PRODUCT: 'vendor-software-product', + GENERAL: 'general', + PROCESS_DETAILS: 'process-details', + NETWORKS: 'networks', + DEPENDENCIES: 'dependencies', + ATTACHMENTS: 'attachments', + ACTIVITY_LOG: 'activity-log', + COMPONENTS: 'components', + + COMPUTE: 'compute', + LOAD_BALANCING: 'load-balancing', + STORAGE: 'storage', + MONITORING: 'monitoring' +}); - COMPUTE: 'Compute', - LOAD_BALANCING: 'Load Balancing', - STORAGE: 'Storage', - MONITORING: 'Monitoring' +export const forms = keyMirror({ + VENDOR_SOFTWARE_PRODUCT_DETAILS: 'vendor-software-product-details', }); + +export const PRODUCT_QUESTIONNAIRE = 'product'; + +export const mapScreenToNavigationItem = { + [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_ACTIVITY_LOG]: navigationItems.ACTIVITY_LOG, + [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: navigationItems.DEPENDENCIES, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: 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, +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js index 6d1db1626f..2fde8c2216 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js index 784ac9db84..97988d87f9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -1,26 +1,23 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {combineReducers} from 'redux'; -import {actionTypes} from './SoftwareProductConstants.js'; -import SoftwareProductAttachmentsReducer from './attachments/SoftwareProductAttachmentsReducer.js'; +import {actionTypes, PRODUCT_QUESTIONNAIRE} from './SoftwareProductConstants.js'; +import HeatValidationReducer from './attachments/validation/HeatValidationReducer.js'; +import HeatSetupReducer from './attachments/setup/HeatSetupReducer.js'; +import {actionTypes as heatSetupActionTypes} from './attachments/setup/HeatSetupConstants.js'; import SoftwareProductCreationReducer from './creation/SoftwareProductCreationReducer.js'; import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js'; import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js'; @@ -35,30 +32,40 @@ import {actionTypes as componentProcessesActionTypes} from './components/proces import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js'; import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; +import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; +import SoftwareProductDependenciesReducer from './dependencies/SoftwareProductDependenciesReducer.js'; +import {createJSONSchemaReducer, createComposedJSONSchemaReducer} from 'sdc-app/common/reducers/JSONSchemaReducer.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; export default combineReducers({ - softwareProductAttachments: SoftwareProductAttachmentsReducer, - softwareProductCreation: SoftwareProductCreationReducer, - softwareProductEditor: SoftwareProductDetailsReducer, + softwareProductAttachments: combineReducers({ + heatValidation: HeatValidationReducer, + heatSetup: HeatSetupReducer, + heatSetupCache: (state = {}, action) => action.type === heatSetupActionTypes.FILL_HEAT_SETUP_CACHE ? action.payload : state + }), + softwareProductCreation: createPlainDataReducer(SoftwareProductCreationReducer), + softwareProductEditor: createPlainDataReducer(SoftwareProductDetailsReducer), softwareProductProcesses: combineReducers({ processesList: SoftwareProductProcessesListReducer, - processesEditor: SoftwareProductProcessesEditorReducer, + processesEditor: createPlainDataReducer(SoftwareProductProcessesEditorReducer), processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state }), softwareProductNetworks: combineReducers({ networksList: SoftwareProductNetworksListReducer }), + softwareProductDependencies: SoftwareProductDependenciesReducer, softwareProductComponents: combineReducers({ componentsList: SoftwareProductComponentsListReducer, - componentEditor: SoftwareProductComponentEditorReducer, + componentEditor: createPlainDataReducer(createComposedJSONSchemaReducer(COMPONENTS_QUESTIONNAIRE, SoftwareProductComponentEditorReducer)), componentProcesses: combineReducers({ processesList: SoftwareProductComponentProcessesListReducer, - processesEditor: SoftwareProductComponentProcessesEditorReducer, + processesEditor: createPlainDataReducer(SoftwareProductComponentProcessesEditorReducer), processToDelete: (state = false, action) => action.type === componentProcessesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM ? action.processToDelete : state, }), network: combineReducers({ nicList: SoftwareProductComponentsNICListReducer, - nicEditor: SoftwareProductComponentsNICEditorReducer + nicEditor: createPlainDataReducer(createComposedJSONSchemaReducer(NIC_QUESTIONNAIRE, SoftwareProductComponentsNICEditorReducer)) }), monitoring: SoftwareProductComponentsMonitoringReducer }), @@ -68,13 +75,5 @@ export default combineReducers({ } return state; }, - softwareProductQuestionnaire: (state = {}, action) => { - if (action.type === actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE) { - return { - ...state, - ...action.payload - }; - } - return state; - } + softwareProductQuestionnaire: createJSONSchemaReducer(PRODUCT_QUESTIONNAIRE) }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js index a4b95a4b7e..8f2506abdd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js @@ -1,42 +1,89 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import HeatSetupActionHelper from './setup/HeatSetupActionHelper.js'; import SoftwareProductAttachmentsView from './SoftwareProductAttachmentsView.jsx'; -import SoftwareProductAttachmentsActionHelper from './SoftwareProductAttachmentsActionHelper.js'; +import {errorLevels} from 'sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import HeatSetup from './setup/HeatSetup.js'; +import {doesHeatDataExist} from './SoftwareProductAttachmentsUtils.js'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = (state) => { + let { + softwareProduct: { + softwareProductEditor:{data: currentSoftwareProduct = {}}, + softwareProductAttachments: {heatSetup, heatSetupCache, heatValidation : {errorList}} + } + } = state; + + let {unassigned = [], modules = []} = heatSetup; + let goToOverview = true; + if (errorList) { + for (let i = 0 ; i < errorList.length ; i++) { + if (errorList[i].level === errorLevels.ERROR) { + goToOverview = false; + } + } + } + let heatDataExist = doesHeatDataExist(heatSetup); -export const mapStateToProps = ({softwareProduct: {softwareProductAttachments}}) => { - let {attachmentsTree, hoveredNode, selectedNode, errorList} = softwareProductAttachments; + let isReadOnlyMode = currentSoftwareProduct && currentSoftwareProduct.version ? + VersionControllerUtils.isReadOnly(currentSoftwareProduct) : false; + let {version} = currentSoftwareProduct; return { - attachmentsTree, - hoveredNode, - selectedNode, - errorList + isValidationAvailable: unassigned.length === 0 && modules.length > 0, + heatSetup, + heatSetupCache, + heatDataExist, + goToOverview, + HeatSetupComponent: HeatSetup, + isReadOnlyMode, + version }; }; -const mapActionsToProps = (dispatch) => { +export const mapActionsToProps = (dispatch, {softwareProductId}) => { return { - toggleExpanded: (path) => SoftwareProductAttachmentsActionHelper.toggleExpanded(dispatch, {path}), - onSelectNode: (nodeName) => SoftwareProductAttachmentsActionHelper.onSelectNode(dispatch, {nodeName}), - onUnselectNode: () => SoftwareProductAttachmentsActionHelper.onUnselectNode(dispatch) + onDownload: ({heatCandidate, isReadOnlyMode, version}) => SoftwareProductActionHelper.downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}), + onUpload: (formData, version) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Upload will erase existing data. Do you want to continue?'), + confirmationButtonText: i18n('Continue'), + onConfirmed: ()=>SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed'), + version + }) + } + }), + onSave: (heatCandidate, version) => SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}), + onGoToOverview: () => { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId}); + }, + onProcessAndValidate: ({heatData, heatDataCache, isReadOnlyMode, version}) => { + return HeatSetupActionHelper.processAndValidateHeat(dispatch, + {softwareProductId, heatData, heatDataCache, isReadOnlyMode, version}); + } }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js index 33af476d9c..b0410d1566 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js @@ -1,55 +1,19 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - -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 -}); - +export const tabsMapping = { + SETUP: 1, + VALIDATION: 2 +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsUtils.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsUtils.js new file mode 100644 index 0000000000..2e76b11630 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsUtils.js @@ -0,0 +1,25 @@ +/*! + * 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. + */ + +export function doesHeatDataExist(heatData) { + let result = false; + for (let key of Object.keys(heatData)) { + if(heatData[key].length > 0) { + result = true; + } + } + return result; +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx index c52999ca46..66fb2f8356 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx @@ -1,182 +1,119 @@ -import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import classNames from 'classnames'; -import Collapse from 'react-bootstrap/lib/Collapse.js'; - +/*! + * 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. + */ +import React, {Component, PropTypes} from 'react'; +import Tabs from 'react-bootstrap/lib/Tabs.js'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import {tabsMapping} from './SoftwareProductAttachmentsConstants.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' -}); +import Icon from 'nfvo-components/icon/Icon.jsx'; +import HeatValidation from './validation/HeatValidation.js'; -const leftPanelWidth = 250; - -class SoftwareProductAttachmentsView extends React.Component { +class HeatScreenView extends Component { static propTypes = { - attachmentsTree: React.PropTypes.object.isRequired + isValidationAvailable: PropTypes.bool, + goToOverview: PropTypes.bool }; + state = { - treeWidth: '400' + activeTab: tabsMapping.SETUP }; render() { - let {attachmentsTree, errorList} = this.props; - let {treeWidth} = this.state; + let {isValidationAvailable, isReadOnlyMode, heatDataExist, onDownload, softwareProductId, onProcessAndValidate, heatSetup, HeatSetupComponent, onGoToOverview, version, ...other} = this.props; 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 className='vsp-attachments-view'> + <div className='attachments-view-controllers'> + {(this.state.activeTab === tabsMapping.SETUP) && + <Icon + iconClassName={heatDataExist ? '' : 'disabled'} + className={heatDataExist ? '' : 'disabled'} + image='download' + label={i18n('Download HEAT')} + onClick={heatDataExist ? () => onDownload({heatCandidate: heatSetup, isReadOnlyMode, version}) : undefined} + data-test-id='download-heat'/>} + {(this.state.activeTab === tabsMapping.VALIDATION && softwareProductId) && + <Icon + iconClassName={this.props.goToOverview ? '' : 'disabled'} + className={`go-to-overview-icon ${this.props.goToOverview ? '' : 'disabled'}`} + labelClassName='go-to-overview-label' + onClick={this.props.goToOverview ? onGoToOverview : undefined} + image='go-to-overview' + label={i18n('Go to Overview')} + data-test-id='go-to-overview'/>} + <Icon + image='upload' + label={i18n('Upload New HEAT')} + className={isReadOnlyMode ? 'disabled' : ''} + iconClassName={isReadOnlyMode ? 'disabled' : ''} + onClick={evt => {this.refs.hiddenImportFileInput.click(evt);}} + data-test-id='upload-heat'/> + <input + ref='hiddenImportFileInput' + type='file' + name='fileInput' + accept='.zip' + onChange={evt => this.handleImport(evt)}/> </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> - } + <Tabs id='attachments-tabs' activeKey={this.state.activeTab} onSelect={key => this.handleTabPress(key)}> + <Tab eventKey={tabsMapping.SETUP} title='HEAT Setup'> + <HeatSetupComponent + heatDataExist={heatDataExist} + changeAttachmentsTab={tab => this.setState({activeTab: tab})} + onProcessAndValidate={onProcessAndValidate} + softwareProductId={softwareProductId} + isReadOnlyMode={isReadOnlyMode} + version={version}/> + </Tab> + <Tab eventKey={tabsMapping.VALIDATION} title='Heat Validation' disabled={!isValidationAvailable}> + <HeatValidation {...other}/> + </Tab> + </Tabs> </div> ); } - createErrorList(errorList, node, parent) { - if (node.errors) { - node.errors.forEach(error => errorList.push({ - error, - name: node.name, - parentName: parent.name, - type: node.type - })); + handleTabPress(key) { + let {heatSetup, heatSetupCache, onProcessAndValidate, isReadOnlyMode, version} = this.props; + switch (key) { + case tabsMapping.VALIDATION: + onProcessAndValidate({heatData: heatSetup, heatDataCache: heatSetupCache, isReadOnlyMode, version}).then( + () => this.setState({activeTab: tabsMapping.VALIDATION}) + ); + return; + case tabsMapping.SETUP: + this.setState({activeTab: tabsMapping.SETUP}); + return; } - 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 - }); + handleImport(evt) { + evt.preventDefault(); + let {version} = this.props; + let formData = new FormData(); + formData.append('upload', this.refs.hiddenImportFileInput.files[0]); + this.refs.hiddenImportFileInput.value = ''; + this.props.onUpload(formData, version); + this.setState({activeTab: tabsMapping.SETUP}); } - getTreeTextClassName(node) { - let {selectedNode} = this.props; - return classNames({ - 'tree-element-text': true, - 'error-status': node.errors, - 'error-status-selected': node.name === selectedNode - }); + save() { + return this.props.onSave(this.props.heatSetup, this.props.version); } - 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; +export default HeatScreenView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetup.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetup.js new file mode 100644 index 0000000000..4c3adc6a7d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetup.js @@ -0,0 +1,62 @@ +/*! + * 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. + */ +import {connect} from 'react-redux'; +import HeatSetupView from './HeatSetupView.jsx'; +import HeatSetupActionHelper from './HeatSetupActionHelper.js'; + + +const BASE = true; + +function baseExists(modules) { + for (let i in modules) { + if (modules[i].isBase) { + return true; + } + } + return false; +} + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments: {heatSetup, heatSetupCache}}}) => { + let {modules = [], unassigned = [], artifacts = [], nested = []} = heatSetup; + let isBaseExist = baseExists(modules); + + return { + heatSetupCache, + modules, + unassigned, + artifacts, + nested, + isBaseExist + }; +}; + +export const mapActionsToProps = (dispatch, {}) => { + return { + onModuleRename: (oldName, newName) => HeatSetupActionHelper.renameModule(dispatch, {oldName, newName}), + onModuleAdd: () => HeatSetupActionHelper.addModule(dispatch, !BASE), + onBaseAdd: () => HeatSetupActionHelper.addModule(dispatch, BASE), + onModuleDelete: moduleName => HeatSetupActionHelper.deleteModule(dispatch, moduleName), + onModuleFileTypeChange: ({module, value, type}) => HeatSetupActionHelper.changeModuleFileType(dispatch, { + module, + value, + type + }), + onArtifactListChange: artifacts => HeatSetupActionHelper.changeArtifactList(dispatch, artifacts), + onAddAllUnassigned: () => HeatSetupActionHelper.addAllUnassignedFilesToArtifacts(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(HeatSetupView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js new file mode 100644 index 0000000000..53143647a3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js @@ -0,0 +1,77 @@ +/*! + * 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. + */ +import {actionTypes} from './HeatSetupConstants.js'; +import isEqual from 'lodash/isEqual.js'; +import cloneDeep from 'lodash/cloneDeep.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; + +export default { + + addModule(dispatch, isBase){ + dispatch({type: actionTypes.ADD_MODULE, data: {isBase}}); + }, + + deleteModule(dispatch, moduleName){ + dispatch({type: actionTypes.REMOVE_MODULE, data: {moduleName}}); + }, + + renameModule(dispatch, {oldName, newName}){ + dispatch({type: actionTypes.RENAME_MODULE, data: {oldName, newName}}); + }, + + changeModuleFileType(dispatch, {module, value, type}){ + if (!value) { + value = {value: ''}; + } + dispatch({type: actionTypes.FILE_ASSIGN_CHANGED, data: {module, value, type}}); + }, + + changeArtifactList(dispatch, artifacts){ + dispatch({type: actionTypes.ARTIFACT_LIST_CHANGE, data: {artifacts: artifacts.map(artifact => artifact.value)}}); + }, + + processAndValidateHeat(dispatch, {softwareProductId, heatData, heatDataCache, isReadOnlyMode, version}){ + return (isEqual({...heatData, softwareProductId}, heatDataCache) || isReadOnlyMode) ? Promise.resolve() : + SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate: heatData, version}) + .then(() => SoftwareProductActionHelper.processAndValidateHeatCandidate(dispatch, {softwareProductId, version})) + .then(() => dispatch({type: actionTypes.FILL_HEAT_SETUP_CACHE, payload: {...cloneDeep(heatData), softwareProductId}})); + }, + + addAllUnassignedFilesToArtifacts(dispatch){ + dispatch({type: actionTypes.ADD_ALL_UNASSIGNED_TO_ARTIFACTS}); + }, + + heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) { + return new Promise((resolve, reject) => { + if (isEqual({...heatSetup, softwareProductId}, heatSetupCache)) { + resolve(); + } else { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`You have uploaded a new HEAT. If you navigate away or Check-in without proceeding to validation, + Old HEAT zip file will be in use. new HEAT will be ignored. Do you want to continue?`), + confirmationButtonText: i18n('Continue'), + onConfirmed: () => resolve(), + onDeclined: () => reject() + } + }); + } + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js new file mode 100644 index 0000000000..2d6bd574a7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js @@ -0,0 +1,42 @@ +/*! + * 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. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + + ARTIFACT_LIST_CHANGE: null, + ADD_ALL_UNASSIGNED_TO_ARTIFACTS: null, + ADD_ALL_ARTIFACTS_TO_UNASSIGNED: null, + + ADD_MODULE: null, + REMOVE_MODULE: null, + RENAME_MODULE: null, + FILL_HEAT_SETUP_CACHE: null, + FILE_ASSIGN_CHANGED: null, + + MANIFEST_LOADED: null, + + GO_TO_VALIDATION: null, + IN_VALIDATION: null + +}); + +export const fileTypes = { + YAML: {label: 'yaml', regex: /(yaml|yml)/g}, + ENV: {label: 'env', regex: /env/g}, + VOL: {label: 'vol', regex: /(yaml|yml)/g}, + VOL_ENV: {label: 'volEnv', regex: /env/g} +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupReducer.js new file mode 100644 index 0000000000..f49339ce35 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupReducer.js @@ -0,0 +1,124 @@ +/*! + * 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. + */ +import {actionTypes} from './HeatSetupConstants.js'; +import differenceWith from 'lodash/differenceWith.js'; + + +const emptyModule = (isBase, currentLength) => ({ + name: `${isBase ? 'base_' : 'module_'}${currentLength + 1}`, + isBase: isBase +}); + +function syncUnassignedFilesWithArtifactsChanges(unassigned, artifacts, oldArtifacts) { + if (artifacts.length > oldArtifacts.length) { + return differenceWith(unassigned, artifacts, (unassignedFile, artifact) => unassignedFile === artifact); + } + else { + const removedArtifact = differenceWith(oldArtifacts, artifacts, (oldArtifact, artifact) => artifact === oldArtifact); + return [...unassigned, removedArtifact[0]]; + } +} + +function findModuleIndexByName(modules, name) { + return modules.findIndex(module => module.name === name); +} + +function addDeletedModuleFilesToUnassigned(unassigned, deletedModule){ + let files = []; + for(let i in deletedModule){ + if (deletedModule.hasOwnProperty(i)) { + if (typeof deletedModule[i] === 'string' && deletedModule[i] && i !== 'name') { + files.push(deletedModule[i]); + } + } + } + + return unassigned.concat(files); +} + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.MANIFEST_LOADED: + return { + ...state, + ...action.response, + modules: action.response.modules.map(module => ({...module, name: module.name || module.yaml.substring(0, module.yaml.lastIndexOf('.'))})) + }; + case actionTypes.ARTIFACT_LIST_CHANGE: + return { + ...state, + artifacts: action.data.artifacts, + unassigned: syncUnassignedFilesWithArtifactsChanges(state.unassigned, action.data.artifacts, state.artifacts) + }; + case actionTypes.ADD_ALL_UNASSIGNED_TO_ARTIFACTS: + return { + ...state, + artifacts: [...state.artifacts,...state.unassigned], + unassigned: [] + }; + case actionTypes.ADD_ALL_ARTIFACTS_TO_UNASSIGNED: + return { + ...state, + artifacts: [], + unassigned: [...state.unassigned, ...state.artifacts] + }; + case actionTypes.ADD_MODULE: + return { + ...state, + modules: state.modules.concat({...emptyModule(action.data.isBase, state.modules.length)}) + }; + case actionTypes.REMOVE_MODULE: + const moduleIndexToDelete = findModuleIndexByName(state.modules, action.data.moduleName); + let unassigned = addDeletedModuleFilesToUnassigned(state.unassigned, state.modules[moduleIndexToDelete]); + return { + ...state, + unassigned, + modules: [...state.modules.slice(0, moduleIndexToDelete), ...state.modules.slice(moduleIndexToDelete + 1)] + }; + case actionTypes.RENAME_MODULE: + const indexToRename = findModuleIndexByName(state.modules, action.data.oldName); + let moduleToRename = state.modules[indexToRename]; + moduleToRename.name = action.data.newName; + return { + ...state, + modules: [...state.modules.slice(0, indexToRename), moduleToRename, ...state.modules.slice(indexToRename + 1)] + }; + case actionTypes.FILE_ASSIGN_CHANGED: + let {module, value:{value}, type} = action.data; + const moduleIndexToModify = findModuleIndexByName(state.modules, module.name); + let moduleToModify = state.modules[moduleIndexToModify]; + let dumpedFile = moduleToModify[type]; + if (dumpedFile !== value) { + if(value) { + moduleToModify[type] = value; + } + else { + delete moduleToModify[type]; + } + const newUnassignedList = dumpedFile ? [...state.unassigned.filter(file => file !== value), dumpedFile] : state.unassigned.filter(file => file !== value); + return { + ...state, + modules: [...state.modules.slice(0, moduleIndexToModify), moduleToModify, ...state.modules.slice(moduleIndexToModify + 1)], + unassigned: newUnassignedList + }; + } + else { + return state; + } + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx new file mode 100644 index 0000000000..0d8bc58361 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx @@ -0,0 +1,328 @@ +/*! + * 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. + */ +import React, {Component} from 'react'; +import Button from 'react-bootstrap/lib/Button.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import FormControl from 'react-bootstrap/lib/FormControl.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SelectInput from 'nfvo-components/input/SelectInput.jsx'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import {fileTypes} from './HeatSetupConstants.js'; +import {tabsMapping} from '../SoftwareProductAttachmentsConstants.js'; +import {sortable} from 'react-sortable'; + +class ListItem extends Component { + + render() { + return ( + <li {...this.props}>{this.props.children}</li> + ); + } +} + + +const SortableListItem = sortable(ListItem); + +class SortableModuleFileList extends Component { + + state = { + draggingIndex: null, + data: this.props.modules + }; + + + componentWillReceiveProps(nextProps) { + this.setState({data: nextProps.modules}); + } + + render() { + + let {unassigned, onModuleRename, onModuleDelete, onModuleAdd, onBaseAdd, onModuleFileTypeChange, isBaseExist} = this.props; + const childProps = module => ({ + module, + onModuleRename, + onModuleDelete, + onModuleFileTypeChange: (value, type) => onModuleFileTypeChange({module, value, type}), + files: unassigned + }); + let listItems = this.state.data.map(function (item, i) { + return ( + <SortableListItem + key={i} + updateState={data => this.setState(data)} + items={this.state.data} + draggingIndex={this.state.draggingIndex} + sortId={i} + outline='list'><ModuleFile {...childProps(item)} /></SortableListItem> + ); + }, this); + + return ( + <div className='modules-list-wrapper'> + <div className='modules-list-header'> + <div className='modules-list-controllers'> + {!isBaseExist && <Button bsStyle='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} + <Button bsStyle='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> + </div> + </div> + <ul>{listItems}</ul> + </div> + ); + } +} + +const tooltip = (name) => <Tooltip id='tooltip-bottom'>{name}</Tooltip>; +const UnassignedFileList = (props) => { + return ( + <div className='unassigned-files'> + <div className='unassigned-files-title'>{i18n('UNASSIGNED FILES')}</div> + <div className='unassigned-files-list'>{props.children}</div> + </div> + ); +}; + +const EmptyListContent = props => { + let {onClick, heatDataExist} = props; + let displayText = heatDataExist ? 'All Files Are Assigned' : ''; + return ( + <div className='go-to-validation-button-wrapper'> + <div className='all-files-assigned'>{i18n(displayText)}</div> + {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angle-right'/></div>} + </div> + ); +}; +const UnassignedFile = (props) => ( + <OverlayTrigger placement='bottom' overlay={tooltip(props.name)} delayShow={1000}> + <li data-test-id='unassigned-files' className='unassigned-files-list-item'>{props.name}</li> + </OverlayTrigger> +); + +const AddOrDeleteVolumeFiels = ({add = true, onAdd, onDelete}) => { + const displayText = add ? 'Add Volume Files' : 'Delete Volume Files'; + const action = add ? onAdd : onDelete; + return ( + <div className='add-or-delete-volumes' onClick={action}> + <SVGIcon name={add ? 'plus' : 'close'} /> + <span>{i18n(displayText)}</span> + </div> + ); +}; + +const SelectWithFileType = ({type, selected, files, onChange}) => { + + let filteredFiledAccordingToType = files.filter(file => file.label.search(type.regex) > -1); + if (selected) { + filteredFiledAccordingToType = filteredFiledAccordingToType.concat({label: selected, value: selected}); + } + + return ( + <SelectInput + data-test-id={`${type.label}-list`} + label={type.label} + value={selected} + onChange={value => value !== selected && onChange(value, type.label)} + disabled={filteredFiledAccordingToType.length === 0} + placeholder={filteredFiledAccordingToType.length === 0 ? '' : undefined} + clearable={true} + options={filteredFiledAccordingToType} /> + ); +}; + +class NameEditInput extends Component { + componentDidMount() { + this.input.focus(); + } + + render() { + return ( + <FormControl {...this.props} className='name-edit' inputRef={input => this.input = input}/> + ); + } +} + +class ModuleFile extends Component { + constructor(props) { + super(props); + this.state = { + isInNameEdit: false, + displayVolumes: Boolean(props.module.vol || props.module.volEnv) + }; + } + + handleSubmit(event, name) { + if (event.keyCode === 13) { + this.handleModuleRename(event, name); + } + } + + componentWillReceiveProps(nextProps) { + this.setState({displayVolumes: Boolean(nextProps.module.vol || nextProps.module.volEnv)}); + } + + handleModuleRename(event, name) { + this.setState({isInNameEdit: false}); + this.props.onModuleRename(name, event.target.value); + } + + deleteVolumeFiles() { + const { onModuleFileTypeChange} = this.props; + onModuleFileTypeChange(null, fileTypes.VOL.label); + onModuleFileTypeChange(null, fileTypes.VOL_ENV.label); + this.setState({displayVolumes: false}); + } + + renderNameAccordingToEditState() { + const {module: {name}} = this.props; + if (this.state.isInNameEdit) { + return (<NameEditInput defaultValue={name} onBlur={evt => this.handleModuleRename(evt, name)} onKeyDown={evt => this.handleSubmit(evt, name)}/>); + } + return (<span className='filename-text'>{name}</span>); + } + + render() { + const {module: {name, isBase, yaml, env, vol, volEnv}, onModuleDelete, files, onModuleFileTypeChange} = this.props; + const {displayVolumes} = this.state; + const moduleType = isBase ? 'BASE' : 'MODULE'; + return ( + <div className='modules-list-item' data-test-id='module-item'> + <div className='modules-list-item-controllers'> + <div className='modules-list-item-filename'> + <Icon image={isBase ? 'base' : 'module'} iconClassName='heat-setup-module-icon' /> + <span className='module-title-by-type'>{`${moduleType}: `}</span> + <div className={`text-and-icon ${this.state.isInNameEdit ? 'in-edit' : ''}`}> + {this.renderNameAccordingToEditState()} + {!this.state.isInNameEdit && <SVGIcon + name='pencil' + onClick={() => this.setState({isInNameEdit: true})} + data-test-id={isBase ? 'base-name' : 'module-name'}/>} + </div> + </div> + <SVGIcon name='trash-o' onClick={() => onModuleDelete(name)} data-test-id='module-delete'/> + </div> + <div className='modules-list-item-selectors'> + <SelectWithFileType + type={fileTypes.YAML} + files={files} + selected={yaml} + onChange={onModuleFileTypeChange}/> + <SelectWithFileType + type={fileTypes.ENV} + files={files} + selected={env} + onChange={onModuleFileTypeChange}/> + {displayVolumes && <SelectWithFileType + type={fileTypes.VOL} + files={files} + selected={vol} + onChange={onModuleFileTypeChange}/>} + {displayVolumes && <SelectWithFileType + type={fileTypes.VOL_ENV} + files={files} + selected={volEnv} + onChange={onModuleFileTypeChange}/>} + <AddOrDeleteVolumeFiels onAdd={() => this.setState({displayVolumes: true})} onDelete={() => this.deleteVolumeFiles()} add={!displayVolumes}/> + </div> + </div> + ); + } +} + +class ArtifactOrNestedFileList extends Component { + + render() { + let {type, title, selected, options, onSelectChanged, onAddAllUnassigned} = this.props; + return ( + <div className={`artifact-files ${type === 'nested' ? 'nested' : ''}`}> + <div className='artifact-files-header'> + <span> + {type === 'artifact' && (<Icon image='artifacts' iconClassName='heat-setup-module-icon' />)} + {`${title}`} + </span> + {type === 'artifact' && <span className='add-all-unassigned' onClick={onAddAllUnassigned}>{i18n('Add All Unassigned Files')}</span>} + </div> + {type === 'nested' ? ( + <ul className='nested-list'>{selected.map(nested => + <li key={nested} className='nested-list-item'>{nested}</li> + )}</ul>) : + (<SelectInput + options={options} + onMultiSelectChanged={onSelectChanged || (() => { + })} + value={selected} + clearable={false} + placeholder={i18n('Add Artifact')} + multi/>) + } + </div> + ); + } +} + +const buildLabelValueObject = str => (typeof str === 'string' ? {value: str, label: str} : str); + +class SoftwareProductHeatSetupView extends Component { + + processAndValidateHeat(heatData, heatDataCache){ + let {onProcessAndValidate, changeAttachmentsTab, version} = this.props; + onProcessAndValidate({heatData, heatDataCache, version}).then( + () => changeAttachmentsTab(tabsMapping.VALIDATION) + ); + } + + render() { + let {modules, heatSetupCache, isReadOnlyMode, heatDataExist, unassigned, artifacts, nested, onArtifactListChange, onAddAllUnassigned} = this.props; + + const formattedUnassigned = unassigned.map(buildLabelValueObject); + const formattedArtifacts = artifacts.map(buildLabelValueObject); + return ( + <div className={`heat-setup-view ${isReadOnlyMode ? 'disabled' : ''}`}> + <div className='heat-setup-view-modules-and-artifacts'> + <SortableModuleFileList + {...this.props} + artifacts={formattedArtifacts} + unassigned={formattedUnassigned}/> + <ArtifactOrNestedFileList + type={'artifact'} + title={i18n('ARTIFACTS')} + options={formattedUnassigned} + selected={formattedArtifacts} + onSelectChanged={onArtifactListChange} + onAddAllUnassigned={onAddAllUnassigned}/> + <ArtifactOrNestedFileList + type={'nested'} + title={i18n('NESTED HEAT FILES')} + options={[]} + selected={nested}/> + </div> + <UnassignedFileList> + { + formattedUnassigned.length > 0 ? + (<ul>{formattedUnassigned.map(file => <UnassignedFile key={file.label} name={file.label}/>)}</ul>) + : + (<EmptyListContent + heatDataExist={heatDataExist} + onClick={() => this.processAndValidateHeat({modules, unassigned, artifacts, nested}, heatSetupCache)}/>) + } + </UnassignedFileList> + </div> + ); + } + +} + +export default SoftwareProductHeatSetupView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js new file mode 100644 index 0000000000..21f6e6c77f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js @@ -0,0 +1,51 @@ +/*! + * 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. + */ +import {connect} from 'react-redux'; +import HeatValidationView from './HeatValidationView.jsx'; +import HeatValidationActionHelper from './HeatValidationActionHelper.js'; +import {errorLevels, nodeFilters} from './HeatValidationConstants.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments: {heatValidation}}}) => { + let {attachmentsTree, selectedNode, errorList} = heatValidation; + let currentErrors = [], currentWarnings = []; + if (errorList) { + for (let i = 0 ; i < errorList.length ; i++) { + if (errorList[i].level === errorLevels.ERROR && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) { + currentErrors[currentErrors.length] = errorList[i]; + } + if (errorList[i].level === errorLevels.WARNING && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) { + currentWarnings[currentWarnings.length] = errorList[i]; + } + } + } + return { + attachmentsTree, + selectedNode, + errorList, + currentErrors, + currentWarnings + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + toggleExpanded: (path) => HeatValidationActionHelper.toggleExpanded(dispatch, {path}), + onSelectNode: (nodeName) => HeatValidationActionHelper.onSelectNode(dispatch, {nodeName}), + onDeselectNode: () => HeatValidationActionHelper.onDeselectNode(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(HeatValidationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js index a7f7a5173b..73366c20cc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js @@ -1,24 +1,19 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; +import {actionTypes} from './HeatValidationConstants.js'; export default { @@ -36,7 +31,7 @@ export default { }); }, - onUnselectNode(dispatch) { + onDeselectNode(dispatch) { dispatch({ type: actionTypes.UNSELECTED_NODE }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js new file mode 100644 index 0000000000..f783fe6482 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js @@ -0,0 +1,57 @@ +/*! + * 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. + */ +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 errorLevels = keyMirror({ + WARNING: 'WARNING', + ERROR: 'ERROR' +}); +export const nodeFilters = keyMirror({ + ALL: 'All' +}); +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/validation/HeatValidationReducer.js index 5c5567b032..f0c10ed457 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js @@ -1,25 +1,20 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; +import {actionTypes, nodeFilters} from './HeatValidationConstants.js'; const mapVolumeData = ({fileName, env, errors}) => ({ name: fileName, @@ -80,7 +75,7 @@ const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, function createErrorList(node, parent, deep = 0, errorList = []) { if (node.errors) { errorList.push(...node.errors.map((error) => ({ - errorLevel: error.level, + level: error.level, errorMessage: error.message, name: node.name, hasParent: deep > 2, @@ -95,14 +90,15 @@ function createErrorList(node, parent, deep = 0, errorList = []) { } const mapValidationDataToTree = validationData => { - let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {}; + let {heat, volume, network, artifacts, other} = validationData.importStructure || {}; return { children: [ { name: 'HEAT', expanded: true, type: 'heat', - children: (HEAT ? HEAT.map(mapHeatData) : []) + header: true, + children: (heat ? heat.map(mapHeatData) : []) }, ...(artifacts ? [{ name: 'artifacts', @@ -174,7 +170,8 @@ export default (state = {attachmentsTree: {}}, action) => { return { ...state, attachmentsTree, - errorList + errorList, + selectedNode: nodeFilters.ALL }; case actionTypes.TOGGLE_EXPANDED: return { @@ -191,7 +188,7 @@ export default (state = {attachmentsTree: {}}, action) => { case actionTypes.UNSELECTED_NODE: return { ...state, - selectedNode: undefined + selectedNode: nodeFilters.ALL }; default: return state; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx new file mode 100644 index 0000000000..25ad90f351 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx @@ -0,0 +1,274 @@ +/*! + * 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. + */ +import React, {Component, PropTypes} from 'react'; +import classNames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js'; + +const leftPanelWidth = 250; +const typeToIcon = Object.freeze({ + heat: 'heat', + volume: 'volume', + network: 'network', + artifact: 'validation-artifacts', + env: 'env', + other: 'validation-other' +}); + + +class HeatValidationView extends Component { + + static propTypes = { + attachmentsTree: PropTypes.object.isRequired, + errorList: PropTypes.array.isRequired, + currentErrors: PropTypes.array.isRequired, + currentWarnings: PropTypes.array.isRequired, + onSelectNode: PropTypes.func.isRequired, + onDeselectNode: PropTypes.func.isRequired, + toggleExpanded: PropTypes.func.isRequired, + selectedNode: PropTypes.string + }; + + render() { + return (<div className='vsp-attachments-heat-validation' data-test-id='heat-validation-editor'> + <HeatFileTree errorList={this.props.errorList} attachmentsTree={this.props.attachmentsTree} + onSelectNode={this.props.onSelectNode} toggleExpanded={this.props.toggleExpanded} + selectedNode={this.props.selectedNode} onDeselectNode={this.props.onDeselectNode} /> + <HeatMessageBoard errors={this.props.currentErrors} warnings={this.props.currentWarnings} selectedNode={this.props.selectedNode} /> + </div> ); + } +} + +function HeatFileTreeRow(props) { + let {node, path, toggleExpanded, selectedNode, selectNode} = props; + let isFolder = node.children && node.children.length > 0; + return ( + <div onDoubleClick={() => toggleExpanded(path)} className={classNames({ + 'tree-node-row': true, + 'tree-node-clicked': node.name === props.selectedNode + })} data-test-id='validation-tree-node'> + <div className='name-section'> + { + isFolder && + <div onClick={() => toggleExpanded(path)} + className='tree-node-expander'> + <SVGIcon name={!node.expanded ? 'chevron-up' : 'chevron-down'} data-test-id='validation-tree-block-toggle'/> + </div> + } + { + + <span className='tree-node-icon'> + <Icon image={typeToIcon[node.type]} iconClassName={selectedNode === node.name ? 'selected' : ''}/> + </span> + } + { + + <span className='tree-node-name' onClick={() => selectNode(node.name)} data-test-id='validation-tree-node-name'> + {node.name ? node.name : 'UNKNOWN'} + </span> + } + </div> + <ErrorsAndWarningsCount errorList={node.errors} onClick={() => selectNode(node.name)} /> + </div>); +} + +function HeatFileTreeHeader(props) { + let hasErrors = props.errorList.filter(error => error.level === errorLevels.ERROR).length > 0; + return ( + <div onClick={() => props.selectNode(nodeFilters.ALL)} className={classNames({'attachments-tree-header': true, + 'header-selected' : props.selectedNode === nodeFilters.ALL})} data-test-id='validation-tree-header'> + <div className='tree-header-title' > + <Icon image='zip' iconClassName={classNames(props.selectedNode === nodeFilters.ALL ? 'selected' : '', 'header-icon')} /> + <span className={classNames({'tree-header-title-text' : true, + 'tree-header-title-selected' : props.selectedNode === nodeFilters.ALL})}>{i18n(`HEAT${hasErrors ? ' (Draft)' : ''}`)}</span> + </div> + <ErrorsAndWarningsCount errorList={props.errorList} size='large' /> + </div>); +} + +class HeatFileTree extends React.Component { + static propTypes = { + attachmentsTree: PropTypes.object.isRequired, + errorList: PropTypes.array.isRequired, + onSelectNode: PropTypes.func.isRequired, + onDeselectNode: PropTypes.func.isRequired, + toggleExpanded: PropTypes.func.isRequired, + selectedNode: PropTypes.string + }; + state = { + treeWidth: '400' + }; + render() { + let {attachmentsTree} = this.props; + return ( + <div className='validation-tree-section' style={{'width' : this.state.treeWidth + 'px'}}> + <div className='vsp-attachments-heat-validation-tree'> + <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='vsp-attachments-heat-validation-separator' data-test-id='validation-tree-separator'></div> + </div>); + } + renderNode(node, path) { + let rand = Math.random() * (3000 - 1) + 1; + let isFolder = node.children && node.children.length > 0; + let {selectedNode} = this.props; + return ( + <div key={node.name + rand} className={classNames({'tree-block-inside' : !node.header})}> + { + node.header ? + <HeatFileTreeHeader selectedNode={selectedNode} errorList={this.props.errorList} selectNode={(nodeName) => this.selectNode(nodeName)} /> : + <HeatFileTreeRow toggleExpanded={this.props.toggleExpanded} node={node} path={path} selectedNode={selectedNode} selectNode={() => this.selectNode(node.name)} /> + } + { + isFolder && + <Collapse in={node.expanded}> + <div className='tree-node-children'> + { + node.children.map((child, ind) => this.renderNode(child, [...path, ind])) + } + </div> + </Collapse> + } + </div> + ); + } + + + + + + selectNode(currentSelectedNode) { + let {onDeselectNode, onSelectNode, selectedNode} = this.props; + if (currentSelectedNode !== selectedNode) { + onSelectNode(currentSelectedNode); + } else { + onDeselectNode(); + } + + + + } + + 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); + } + } +} + +class HeatMessageBoard extends Component { + static propTypes = { + currentErrors: PropTypes.array, + currentWarnings: PropTypes.array, + selectedNode: PropTypes.string + }; + render() { + let {errors, warnings} = this.props; + let allItems = [...errors, ...warnings]; + return ( + <div className='message-board-section'> + { allItems.map(error => this.renderError(error)) } + </div> + ); + } + renderError(error) { + let rand = Math.random() * (3000 - 1) + 1; + return ( + <div + key={error.name + error.errorMessage + error.parentName + rand} + className='error-item' data-test-id='validation-error'> + {error.level === errorLevels.WARNING ? + <SVGIcon name='exclamation-triangle-line' iconClassName='large' /> : <Icon image='error-lg' /> } + <span className='error-item-file-type'> + { + (this.props.selectedNode === nodeFilters.ALL) ? + <span> + <span className='error-file-name'> + {i18n('{errorName}:', { + errorName: error.name + })} + </span> + <span> + {i18n('{message}', {message: error.errorMessage})} + </span> + </span> : + i18n('{errorMsg}', { + errorMsg: error.errorMessage + }) + } + </span> + </div> + ); + } +} +class ErrorsAndWarningsCount extends Component { + static propTypes = { + errorList: PropTypes.array, + size: PropTypes.string + }; + render() { + let errors = this.getErrorsAndWarningsCount(this.props.errorList); + if (!errors) { + return null; + } + let errIcon = 'error'; + let {size} = this.props; + if (size && size === 'large') { + errIcon += '-lg'; + } + return (<div className='counters'> + {(errors.errorCount > 0) && <div className='counter'> + <Icon image={errIcon} iconClassName='counter-icon'/> + <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>{errors.errorCount}</div> + </div>} + {(errors.warningCount > 0) && <div className='counter'> + <SVGIcon name='exclamation-triangle-line' iconClassName={size} /> + <div className={'warning-text ' + (size ? size : '')} data-test-id='validation-warning-count'>{errors.warningCount}</div> + </div>} + </div>); + } + getErrorsAndWarningsCount(errorList) { + let errorCount = 0, warningCount = 0; + if (errorList && errorList.length > 0) { + for (let i = 0; i < errorList.length; i++) { + if (errorList[i].level === errorLevels.ERROR) { + errorCount++; + } else if (errorList[i].level === errorLevels.WARNING) { + warningCount++; + } + } + } + if (errorCount === 0 && warningCount === 0) { + return null; + } + return {errorCount, warningCount}; + } +} +export default HeatValidationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js index 3b8bc4f171..2ae9ad0bae 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js @@ -1,44 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from './SoftwareProductComponentsConstants.js'; +import {actionTypes, forms} 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: + case actionTypes.COMPONENT_LOAD: return { ...state, - data: { - ...state.data, - ...action.deltaData + formReady: null, + formName: forms.ALL_SPC_FORMS, + genericFieldInfo: { + 'displayName' : { + isValid: true, + errorText: '', + validations: [] + }, + 'vfcCode' : { + isValid: true, + errorText: '', + validations: [] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [] + } } }; default: diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js index e53b2ecafe..9b3c9eaa73 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -1,56 +1,51 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -import {actionTypes} from './SoftwareProductComponentsConstants.js'; +import {actionTypes, COMPONENTS_QUESTIONNAIRE, forms} from './SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; -function baseUrl(softwareProductId) { +function baseUrl(softwareProductId, version) { + const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components`; + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components`; } function fetchSoftwareProductComponents(softwareProductId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); } -function putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, vspComponent) { - return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire`, vspComponent); +function putSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId, vspComponent) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${vspComponentId}/questionnaire`, vspComponent); } -function fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version){ - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire${versionQuery}`); +function fetchSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId){ + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}/${vspComponentId}/questionnaire`); } -function fetchSoftwareProductComponent(softwareProductId, vspComponentId, version){ - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}${versionQuery}`); +function fetchSoftwareProductComponent(softwareProductId, version, vspComponentId){ + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}/${vspComponentId}`); } -function putSoftwareProductComponent(softwareProductId, vspComponentId, vspComponent) { - return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}`, { +function putSoftwareProductComponent(softwareProductId, version, vspComponentId, vspComponent) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${vspComponentId}`, { name: vspComponent.name, displayName: vspComponent.displayName, + vfcCode: vspComponent.vfcCode, description: vspComponent.description }); } @@ -65,27 +60,19 @@ const SoftwareProductComponentsActionHelper = { }); }, - componentDataChanged(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.COMPONENT_DATA_CHANGED, - deltaData - }); - }, - - - updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, componentData, qdata}) { + updateSoftwareProductComponent(dispatch, {softwareProductId, version, vspComponentId, componentData, qdata}) { return Promise.all([ - SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}), - SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId, qdata}), + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, version, vspComponentId, componentData}) ]); }, - updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}) { - return putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, qdata); + updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId, qdata}) { + return putSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId, qdata); }, - updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) { - return putSoftwareProductComponent(softwareProductId, vspComponentId, componentData).then(() => dispatch({ + updateSoftwareProductComponentData(dispatch, {softwareProductId, version, vspComponentId, componentData}) { + return putSoftwareProductComponent(softwareProductId, version, vspComponentId, componentData).then(() => dispatch({ type: actionTypes.COMPONENTS_LIST_EDIT, component: { id: vspComponentId, @@ -94,36 +81,36 @@ const SoftwareProductComponentsActionHelper = { })); }, - - 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) - } - }); + fetchSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId}) { + return fetchSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId).then(response => { + ValidationHelper.qDataLoaded(dispatch, {qName: COMPONENTS_QUESTIONNAIRE, response: {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 - }); + fetchSoftwareProductComponent(dispatch, {softwareProductId, version, vspComponentId}) { + dispatch({ + type: actionTypes.COMPONENT_LOAD }); + return Promise.all([ + fetchSoftwareProductComponent(softwareProductId, version, vspComponentId).then(response => { + ValidationHelper.dataChanged(dispatch,{deltaData: response.data, formName: forms.ALL_SPC_FORMS}); + return response; + }), + fetchSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId).then(response => { + ValidationHelper.qDataLoaded(dispatch, {qName: COMPONENTS_QUESTIONNAIRE, response: {qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema)}}); + }) + ]); }, - componentQuestionnaireUpdated(dispatch, {data}) { + + clearComponentsStore(dispatch) { dispatch({ - type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, - payload: { - qdata: data - } + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: [] }); - }, + } }; 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 index dee517e5d1..9307b099ed 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js @@ -1,31 +1,24 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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 + COMPONENT_LOAD: null }); export const storageConstants = keyMirror({ @@ -35,12 +28,18 @@ export const storageConstants = keyMirror({ } }); +export const forms = keyMirror({ + ALL_SPC_FORMS: null, + NIC_EDIT_FORM: null +}); + +export const COMPONENTS_QUESTIONNAIRE = 'component'; + 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 index f1c1ed1fcc..f789a92c6f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import SoftwareProductComponentsListView from './SoftwareProductComponentsListView.jsx'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; @@ -37,9 +32,9 @@ const mapStateToProps = ({softwareProduct}) => { }; -const mapActionToProps = (dispatch, {version}) => { +const mapActionToProps = (dispatch) => { return { - onComponentSelect: ({id: softwareProductId, componentId}) => { + onComponentSelect: ({id: softwareProductId, componentId, version}) => { OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js index b91362a0cf..c7aaca5573 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductComponentsConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx index 0c0ba0f646..c28831fbde 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx @@ -1,8 +1,24 @@ +/*! + * 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. + */ 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'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; const ComponentPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -43,8 +59,9 @@ class SoftwareProductComponentsListView extends React.Component { title={i18n('Virtual Function Components')} filterValue={localFilter} placeholder={i18n('Filter Components')} - onFilter={filter => this.setState({localFilter: filter})} - isReadOnlyMode={isReadOnlyMode}> + onFilter={value => this.setState({localFilter: value})} + isReadOnlyMode={isReadOnlyMode} + twoColumns> {this.filterList().map(component => this.renderComponentsListItem(component))} </ListEditorView> ); @@ -52,20 +69,18 @@ class SoftwareProductComponentsListView extends React.Component { renderComponentsListItem(component) { let {id: componentId, name, displayName, description = ''} = component; - let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; + let {currentSoftwareProduct: {id, version}, 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> + onSelect={() => onComponentSelect({id, componentId, version})}> + <ListEditorItemViewField> <div className='name'>{displayName}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Description')}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> <div className='description'>{description}</div> - </div> + </ListEditorItemViewField> </ListEditorItemView> ); } 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 index 7ac1c707ab..e97477b54d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js @@ -1,51 +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 - * + * + * 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========================================================= + * 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. */ - 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'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let {componentEditor: {qdata, dataMap, qgenericFieldInfo}} = 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 + dataMap, + qgenericFieldInfo, + isReadOnlyMode }; }; -const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + onQDataChanged: (deltaData, customValidations) => ValidationHelper.qDataChanged(dispatch, {deltaData, customValidations: customValidations, + qName: COMPONENTS_QUESTIONNAIRE}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata});}, + qValidateData: (data, customValidations) => ValidationHelper.qValidateData(dispatch, {data, customValidations: customValidations, + qName: COMPONENTS_QUESTIONNAIRE}) }; }; 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 index 3bad147117..8c197f0d49 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx @@ -1,128 +1,71 @@ +/*! + * 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. + */ 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 Form from 'nfvo-components/input/validation/Form.jsx'; +import VmSizing from './computeComponents/VmSizing.jsx'; +import NumberOfVms from './computeComponents/NumberOfVms.jsx'; +import GuestOs from './computeComponents/GuestOs.jsx'; +import Validator from 'nfvo-utils/Validator.js'; class SoftwareProductComponentComputeView extends React.Component { static propTypes = { - qdata: React.PropTypes.object, - qschema: React.PropTypes.object, + dataMap: React.PropTypes.object, + qgenericFieldInfo: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, - minNumberOfVMsSelectedByUser: React.PropTypes.number, onQDataChanged: React.PropTypes.func.isRequired, + qValidateData: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired }; render() { - let {qdata, qschema, isReadOnlyMode, minNumberOfVMsSelectedByUser, onQDataChanged, onSubmit} = this.props; + let {qdata, dataMap, qgenericFieldInfo, isReadOnlyMode, onQDataChanged, qValidateData, onSubmit} = this.props; return ( <div className='vsp-component-questionnaire-view'> - <ValidationForm - ref='computeValidationForm' + { qgenericFieldInfo && <Form + ref={ (form) => { this.form = form; }} + formReady={null} + isValid={true} 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> + isReadOnlyMode={isReadOnlyMode} > + <VmSizing onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> + <NumberOfVms onQDataChanged={onQDataChanged} dataMap={dataMap} + qgenericFieldInfo={qgenericFieldInfo} qValidateData={qValidateData} + customValidations={{'compute/numOfVMs/maximum' : this.validateMax, 'compute/numOfVMs/minimum': this.validateMin}} /> + <GuestOs onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> + </Form> } </div> ); } save(){ - return this.refs.computeValidationForm.handleFormSubmit(new Event('dummy')); + return this.form.handleFormSubmit(new Event('dummy')); + } + + validateMin(value, state) { + let maxVal = state.dataMap['compute/numOfVMs/maximum']; + return Validator.validateItem(value,maxVal,'maximum'); + } + + validateMax(value, state) { + let minVal = state.dataMap['compute/numOfVMs/minimum']; + return Validator.validateItem(value,minVal,'minimum'); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx new file mode 100644 index 0000000000..7a730d6f94 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx @@ -0,0 +1,76 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const GuestOs = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <div> + <GridSection title={i18n('Guest OS')} > + <GridItem colSpan={2}> + <Input + data-test-id='guestOS-name' + label={i18n('Guest OS')} + type='text' + onChange={(tools) => onQDataChanged({'compute/guestOS/name' : tools})} + isValid={qgenericFieldInfo['compute/guestOS/name'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/name'].errorText} + value={dataMap['compute/guestOS/name']} /> + </GridItem> + <GridItem colSpan={2}/> + <GridItem> + <div className='vertical-flex'> + <label key='label' className='control-label'>{i18n('OS Bit Size')}</label> + <div className='radio-options-content-row'> + {qgenericFieldInfo['compute/guestOS/bitSize'].enum.map(bitSize => ( + <Input + data-test-id='guestOS-bitSize' + type='radio' + key={bitSize.enum} + name={'compute/guestOS/bitSize'} + className='radio-field' + value={bitSize.enum} + label={bitSize.title} + onChange={(bit) => onQDataChanged({'compute/guestOS/bitSize' : Number(bit)})} + isValid={qgenericFieldInfo['compute/guestOS/bitSize'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/bitSize'].errorText} + checked={dataMap['compute/guestOS/bitSize'] === bitSize.enum} /> )) } + </div> + </div> + </GridItem> + <GridItem colSpan={2}/> + <GridItem colSpan={2}> + <Input + data-test-id='guestOS-tools' + type='textarea' + label={i18n('Guest OS Tools:')} + onChange={(tools) => onQDataChanged({'compute/guestOS/tools' : tools})} + isValid={qgenericFieldInfo['compute/guestOS/tools'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/tools'].errorText} + value={dataMap['compute/guestOS/tools']} /> + </GridItem> + </GridSection> + + + </div> + ); +}; + +export default GuestOs; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx new file mode 100644 index 0000000000..efeedc653e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx @@ -0,0 +1,94 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const NumberOfVms = ({qgenericFieldInfo, dataMap, onQDataChanged, qValidateData, customValidations}) => { + return( + <GridSection titleClassName='software-product-compute-number-of-vms' title={i18n('NUMBER OF VMs')}> + <GridItem> + <Input + data-test-id='numOfVMs-minimum' + type='number' + label={i18n('Minimum')} + onChange={(tools) => { onQDataChanged({'compute/numOfVMs/minimum' : tools}, customValidations); + qValidateData({'compute/numOfVMs/maximum' : dataMap['compute/numOfVMs/maximum']}, customValidations); } } + isValid={qgenericFieldInfo['compute/numOfVMs/minimum'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/minimum'].errorText} + value={dataMap['compute/numOfVMs/minimum']} /> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-maximum' + type='number' + label={i18n('Maximum')} + onChange={(tools) => { onQDataChanged({'compute/numOfVMs/maximum' : tools}, customValidations); + qValidateData({'compute/numOfVMs/minimum' : dataMap['compute/numOfVMs/minimum']}, customValidations); } } + isValid={qgenericFieldInfo['compute/numOfVMs/maximum'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/maximum'].errorText} + value={dataMap['compute/numOfVMs/maximum']} /> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-CpuOverSubscriptionRatio' + label={i18n('CPU Oversubscription Ratio')} + type='select' + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].errorText} + value={dataMap['compute/numOfVMs/CpuOverSubscriptionRatio']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'compute/numOfVMs/CpuOverSubscriptionRatio' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].enum.map(cpuOSR => <option value={cpuOSR.enum} key={cpuOSR.enum}>{cpuOSR.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-MemoryRAM' + type='select' + label={i18n('Memory - RAM')} + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].errorText} + value={dataMap['compute/numOfVMs/MemoryRAM']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'compute/numOfVMs/MemoryRAM' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].enum.map(mRAM => <option value={mRAM.enum} key={mRAM.enum}>{mRAM.title}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +NumberOfVms.propTypes = { + minNumberOfVMsSelectedByUser: React.PropTypes.number +}; + +export default NumberOfVms; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx new file mode 100644 index 0000000000..39f84807a2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx @@ -0,0 +1,68 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +const VmSizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={i18n('VM Sizing')}> + <GridItem> + <Input + data-test-id='numOfCPUs' + type='number' + label={i18n('Number of CPUs')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/numOfCPUs' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].errorText} + value={dataMap['compute/vmSizing/numOfCPUs']} /> + </GridItem> + <GridItem> + <Input + data-test-id='fileSystemSizeGB' + type='number' + label={i18n('File System Size (GB)')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/fileSystemSizeGB' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].errorText} + value={dataMap['compute/vmSizing/fileSystemSizeGB']} /> + </GridItem> + <GridItem> + <Input + data-test-id='persistentStorageVolumeSize' + type='number' + label={i18n('Persistent Storage/Volume Size (GB)')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/persistentStorageVolumeSize' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].errorText} + value={dataMap['compute/vmSizing/persistentStorageVolumeSize']} /> + </GridItem> + <GridItem> + <Input + data-test-id='IOOperationsPerSec' + type='number' + label={i18n('I/O Operations (per second)')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/IOOperationsPerSec' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].errorText} + value={dataMap['compute/vmSizing/IOOperationsPerSec']} /> + </GridItem> + </GridSection> + ); +}; + +export default VmSizing; 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 index e4c330bec8..34374aa7fb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js @@ -1,50 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import SoftwareProductComponentsGeneralView from './SoftwareProductComponentsGeneralView.jsx'; import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; + + +import {forms, COMPONENTS_QUESTIONNAIRE} from '../SoftwareProductComponentsConstants.js'; + export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {data: componentData = {} , qdata, qschema}} = softwareProductComponents; - + let {componentEditor: {data: componentData = {} , qdata, qgenericFieldInfo : qGenericFieldInfo, dataMap, genericFieldInfo}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); return { componentData, qdata, - qschema, - isReadOnlyMode + isReadOnlyMode, + genericFieldInfo, + qGenericFieldInfo, + dataMap, + isFormValid }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}), - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.ALL_SPC_FORMS}), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, - {softwareProductId, vspComponentId: componentId, componentData, qdata}); - } + {softwareProductId, version, vspComponentId: componentId, componentData, qdata}); + }, + onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}) }; }; 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 index 5d11e42cd3..e4595f97d6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx @@ -1,177 +1,285 @@ +/*! + * 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. + */ 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 Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isReadOnlyMode, genericFieldInfo}) => ( + <GridSection title={i18n('General')}> + {/* 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> + + */} + <GridItem> + <Input + data-test-id='name' + label={i18n('Name')} + value={displayName} + disabled={true} + type='text'/> + <Input + data-test-id='vfcCode' + label={i18n('Naming Code')} + value={vfcCode} + isValid={genericFieldInfo.vfcCode.isValid} + errorText={genericFieldInfo.vfcCode.errorText} + onChange={vfcCode => onDataChanged({vfcCode})} + disabled={isReadOnlyMode} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Description')} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + onChange={description => onDataChanged({description})} + disabled={isReadOnlyMode} + value={description} + groupClassName='multi-line-textarea' + data-test-id='description' + type='textarea'/> + </GridItem> + <GridItem /> + </GridSection> + ); + +const HypervisorSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Hypervisor')}> + <GridItem> + <Input + data-test-id='hypervisor' + label={i18n('Supported Hypervisors')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['general/hypervisor/hypervisor'].isValid} + errorText={qgenericFieldInfo['general/hypervisor/hypervisor'].errorText} + value={dataMap['general/hypervisor/hypervisor']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/hypervisor/hypervisor' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['general/hypervisor/hypervisor'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + <GridItem colSpan={2}> + <Input + data-test-id='drivers' + onChange={(driver) => onQDataChanged({'general/hypervisor/drivers' : driver})} + label={i18n('Hypervisor Drivers')} + type='text' + isValid={qgenericFieldInfo['general/hypervisor/drivers'].isValid} + errorText={qgenericFieldInfo['general/hypervisor/drivers'].errorText} + value={dataMap['general/hypervisor/drivers']}/> + </GridItem> + <GridItem colSpan={3}> + <Input + data-test-id='containerFeaturesDescription' + label={i18n('Describe Container Features')} + type='textarea' + onChange={(containerFeaturesDescription) => onQDataChanged({'general/hypervisor/containerFeaturesDescription' : containerFeaturesDescription})} + isValid={qgenericFieldInfo['general/hypervisor/containerFeaturesDescription'].isValid} + errorText={qgenericFieldInfo['general/hypervisor/containerFeaturesDescription'].errorText} + value={dataMap['general/hypervisor/containerFeaturesDescription']}/> + </GridItem> + </GridSection> +); + +const ImageSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Image')}> + <GridItem> + <Input + data-test-id='format' + label={i18n('Image format')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['general/image/format'].isValid} + errorText={qgenericFieldInfo['general/image/format'].errorText} + value={dataMap['general/image/format']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/image/format' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['general/image/format'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='providedBy' + label={i18n('Image provided by')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['general/image/providedBy'].isValid} + errorText={qgenericFieldInfo['general/image/providedBy'].errorText} + value={dataMap['general/image/providedBy']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/image/providedBy' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['general/image/providedBy'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='bootDiskSizePerVM' + onChange={(bootDiskSizePerVM) => onQDataChanged({'general/image/bootDiskSizePerVM' : bootDiskSizePerVM})} + label={i18n('Size of boot disk per VM (GB)')} + type='number' + isValid={qgenericFieldInfo['general/image/bootDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/image/bootDiskSizePerVM'].errorText} + value={dataMap['general/image/bootDiskSizePerVM']}/> + </GridItem> + <GridItem> + <Input + data-test-id='ephemeralDiskSizePerVM' + onChange={(ephemeralDiskSizePerVM) => onQDataChanged({'general/image/ephemeralDiskSizePerVM' : ephemeralDiskSizePerVM})} + label={i18n('Size of ephemeral disk per VM (GB)')} + type='number' + isValid={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].errorText} + value={dataMap['general/image/ephemeralDiskSizePerVM']}/> + </GridItem> + </GridSection> +); + +const RecoverySection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Recovery')}> + <GridItem> + <Input + data-test-id='pointObjective' + label={i18n('VM Recovery Point Objective (Minutes)')} + type='number' + onChange={(pointObjective) => onQDataChanged({'general/recovery/pointObjective' : pointObjective})} + isValid={qgenericFieldInfo['general/recovery/pointObjective'].isValid} + errorText={qgenericFieldInfo['general/recovery/pointObjective'].errorText} + value={dataMap['general/recovery/pointObjective']}/> + </GridItem> + <GridItem> + <Input + data-test-id='timeObjective' + label={i18n('VM Recovery Time Objective (Minutes)')} + type='number' + onChange={(timeObjective) => onQDataChanged({'general/recovery/timeObjective' : timeObjective})} + isValid={qgenericFieldInfo['general/recovery/timeObjective'].isValid} + errorText={qgenericFieldInfo['general/recovery/timeObjective'].errorText} + value={dataMap['general/recovery/timeObjective']}/> + <div className='empty-two-col' /> + </GridItem> + <GridItem colSpan={2} /> + <GridItem colSpan={2}> + <Input + data-test-id='vmProcessFailuresHandling' + className='textarea' + label={i18n('How are in VM process failures handled?')} + type='textarea' + onChange={(vmProcessFailuresHandling) => onQDataChanged({'general/recovery/vmProcessFailuresHandling' : vmProcessFailuresHandling})} + isValid={qgenericFieldInfo['general/recovery/vmProcessFailuresHandling'].isValid} + errorText={qgenericFieldInfo['general/recovery/vmProcessFailuresHandling'].errorText} + value={dataMap['general/recovery/vmProcessFailuresHandling']}/> + <div className='empty-two-col' /> + + </GridItem> + { + /** disabled until backend will be ready to implement it + <div className='row'> + <div className='col-md-3'> + <Input + label={i18n('VM Recovery Document')} + type='text' + pointer='/general/recovery/VMRecoveryDocument'/> + </div> + </div> + */ + } + </GridSection> +); + +const DNSConfigurationSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('DNS Configuration')}> + <GridItem colSpan={2}> + <Input + data-test-id='dnsConfiguration' + label={i18n('Do you have a need for DNS as a Service? Please describe.')} + type='textarea' + onChange={(dnsConfiguration) => onQDataChanged({'general/dnsConfiguration' : dnsConfiguration})} + isValid={qgenericFieldInfo['general/dnsConfiguration'].isValid} + errorText={qgenericFieldInfo['general/dnsConfiguration'].errorText} + value={dataMap['general/dnsConfiguration']}/> + </GridItem> + </GridSection> +); + +const CloneSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Clone')}> + <GridItem colSpan={2}> + <Input + data-test-id='vmCloneUsage' + label={i18n('Describe VM Clone Use')} + type='textarea' + onChange={(vmCloneUsage) => onQDataChanged({'general/vmCloneUsage' : vmCloneUsage})} + isValid={qgenericFieldInfo['general/vmCloneUsage'].isValid} + errorText={qgenericFieldInfo['general/vmCloneUsage'].errorText} + value={dataMap['general/vmCloneUsage']}/> + </GridItem> + </GridSection> +); class SoftwareProductComponentsGeneralView extends React.Component { render() { - let {qdata, qschema, onQDataChanged, onDataChanged, componentData: {displayName, description}, isReadOnlyMode} = this.props; + let {onQDataChanged, onDataChanged, genericFieldInfo, dataMap, qGenericFieldInfo, componentData: {displayName, vfcCode, 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} + {genericFieldInfo && qGenericFieldInfo && <Form + isValid={this.props.isFormValid} + formReady={null} isReadOnlyMode={isReadOnlyMode} + onValidityChanged={(isValidityData) => this.props.onValidityChanged(isValidityData)} 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> - } + <GeneralSection + onDataChanged={onDataChanged} + displayName={displayName} + vfcCode={vfcCode} + description={description} + isReadOnlyMode={isReadOnlyMode} + genericFieldInfo={genericFieldInfo}/> + <HypervisorSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <ImageSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <RecoverySection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <DNSConfigurationSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <CloneSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + </Form> } </div> </div> ); 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 index 4d4034de5b..0634858219 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js @@ -1,45 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import SoftwareProductComponentLoadBalancingView from './SoftwareProductComponentLoadBalancingRefView.jsx'; import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let {componentEditor: {qdata, qgenericFieldInfo : genericFieldInfo, dataMap}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { qdata, - qschema, + genericFieldInfo, + dataMap, isReadOnlyMode }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata});} }; }; 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 index 1aa2babc12..dc86771400 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -1,11 +1,29 @@ +/*! + * 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. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; 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 Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from'nfvo-components/input/validation/Input.jsx'; -const prefix = '/highAvailabilityAndLoadBalancing/'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const prefix = 'highAvailabilityAndLoadBalancing/'; const pointers = [ { @@ -33,6 +51,31 @@ const pointers = [ } ]; +const TextAreaItem = ({item, toggle, expanded, genericFieldInfo, dataMap, onQDataChanged}) => ( + <GridItem colSpan={3} key={item.key} > + <div className={expanded ? 'title' : 'title add-padding'} + data-test-id={`btn-${item.key}`} + onClick={() => toggle(item.key)}> + <SVGIcon name={expanded ? 'chevron-up' : 'chevron-down'}/> + <span className='title-text'>{i18n(item.description)}</span> + {item.added && <div className='new-line'>{i18n(item.added)}</div>} + </div> + <div className={expanded ? 'collapse in' : 'collapse'}> + <div> + <div> + <Input + data-test-id={`input-${item.key}`} + type='textarea' + isValid={genericFieldInfo[`${prefix}${item.key}`].isValid} + errorText={genericFieldInfo[`${prefix}${item.key}`].errorText} + value={dataMap[`${prefix}${item.key}`]} + onChange={(val) => onQDataChanged({[`${prefix}${item.key}`] : val})} /> + </div> + </div> + </div> + </GridItem> +); + class SoftwareProductComponentLoadBalancingView extends React.Component { static propTypes = { componentId: React.PropTypes.string.isRequired, @@ -46,42 +89,65 @@ class SoftwareProductComponentLoadBalancingView extends React.Component { 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; + let {dataMap, genericFieldInfo, 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} + { genericFieldInfo && <Form + formReady={null} + isValid={true} + onSubmit={() => this.save()} isReadOnlyMode={isReadOnlyMode} hasButtons={false}> - {pointers.map(pointer => this.renderTextAreaItem(pointer))} - </ValidationForm> + <GridSection title={i18n('High Availability & Load Balancing')}> + <GridItem colSpan={1}> + <Input + data-test-id='input-is-component-mandatory' + label={i18n('Is Component Mandatory')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={genericFieldInfo[`${prefix}isComponentMandatory`].isValid} + errorText={genericFieldInfo[`${prefix}isComponentMandatory`].errorText} + value={dataMap[`${prefix}isComponentMandatory`]} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({[`${prefix}isComponentMandatory`] : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + { genericFieldInfo[`${prefix}isComponentMandatory`].enum.map(isMan => <option value={isMan.enum} key={isMan.enum}>{isMan.title}</option>) } + </Input> + </GridItem> + <GridItem colSpan={3}/> + <GridItem colSpan={1}> + <Input + data-test-id='input-high-availability-mode' + label={i18n('High Availability Mode')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={genericFieldInfo[`${prefix}highAvailabilityMode`].isValid} + errorText={genericFieldInfo[`${prefix}highAvailabilityMode`].errorText} + value={dataMap[`${prefix}highAvailabilityMode`]} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({[`${prefix}highAvailabilityMode`] : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {genericFieldInfo[`${prefix}highAvailabilityMode`].enum.map(hmode => <option value={hmode.enum} key={hmode.enum}>{hmode.title}</option>)} + </Input> + </GridItem> + <GridItem colSpan={3}/> + </GridSection> + <GridSection> + {pointers.map(pointer => <TextAreaItem onQDataChanged={onQDataChanged} + genericFieldInfo={genericFieldInfo} dataMap={dataMap} item={pointer} key={pointer.key + 'pKey'} + expanded={this.state.expanded[pointer.key]} toggle={(name)=>{this.toggle(name);}} />)} + </GridSection> + </Form> } </div> </div> ); 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 index ed7c5a116a..293e252dca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js @@ -1,27 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentsMonitoringView from './SoftwareProductComponentsMonitoringView.jsx'; import SoftwareProductComponentsMonitoringAction from './SoftwareProductComponentsMonitoringActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + export const mapStateToProps = ({softwareProduct}) => { @@ -37,11 +35,12 @@ export const mapStateToProps = ({softwareProduct}) => { }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { onDropMibFileToUpload: (formData, type) => SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, { softwareProductId, + version, componentId, formData, type @@ -49,9 +48,18 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, { softwareProductId, + version, componentId, type - }) + }), + + onFileUploadError: () => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + title: i18n('Upload Failed'), + msg: i18n('Expected "zip" file. Please check the provided file type.') + } + }), }; }; 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 index 3faf571c09..64403faa78 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js @@ -1,80 +1,76 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; const UPLOAD = true; -function baseUrl(vspId, componentId) { +function baseUrl(vspId, version, componentId) { + const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${vspId}/components/${componentId}/monitors`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/components/${componentId}/monitors`; } -function snmpTrapUrl(vspId, componentId, isUpload) { - return `${baseUrl(vspId, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; +function snmpTrapUrl(vspId, version, componentId, isUpload) { + return `${baseUrl(vspId, version, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; } -function snmpPollUrl(vspId, componentId, isUpload) { - return `${baseUrl(vspId, componentId)}/snmp${isUpload ? '/upload' : ''}`; +function snmpPollUrl(vspId, version, componentId, isUpload) { + return `${baseUrl(vspId, version, componentId)}/snmp${isUpload ? '/upload' : ''}`; } let onInvalidFileSizeUpload = (dispatch) => dispatch({ - type: NotificationConstants.NOTIFY_ERROR, + type: modalActionTypes.GLOBAL_MODAL_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({ +let uploadSnmpTrapFile = (dispatch, {softwareProductId, version, componentId, formData}) => { + RestAPIUtil.post(snmpTrapUrl(softwareProductId, version, 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({ +let uploadSnmpPollFile = (dispatch, {softwareProductId, version, componentId, formData}) => { + RestAPIUtil.post(snmpPollUrl(softwareProductId, version, 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({ +let deleteSnmpTrapFile = (dispatch, {softwareProductId, version, componentId}) => { + RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ type: actionTypes.SNMP_TRAP_DELETED })); }; -let deleteSnmpPollFile = (dispatch, {softwareProductId, componentId}) => { - RestAPIUtil.destroy(snmpPollUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ +let deleteSnmpPollFile = (dispatch, {softwareProductId, version, componentId}) => { + RestAPIUtil.destroy(snmpPollUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ type: actionTypes.SNMP_POLL_DELETED })); }; const SoftwareProductComponentsMonitoringAction = { - fetchExistingFiles(dispatch, {softwareProductId, componentId}){ - RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/snmp`).then(response => + fetchExistingFiles(dispatch, {softwareProductId, version, componentId}){ + RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/snmp`).then(response => dispatch({ type: actionTypes.SNMP_FILES_DATA_CHANGE, data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll} @@ -82,13 +78,13 @@ const SoftwareProductComponentsMonitoringAction = { ); }, - uploadSnmpFile(dispatch, {softwareProductId, componentId, formData, type}){ + uploadSnmpFile(dispatch, {softwareProductId, version, componentId, formData, type}){ if (formData.get('upload').size) { if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - uploadSnmpTrapFile(dispatch, {softwareProductId, componentId, formData}); + uploadSnmpTrapFile(dispatch, {softwareProductId, version, componentId, formData}); } else { - uploadSnmpPollFile(dispatch, {softwareProductId, componentId, formData}); + uploadSnmpPollFile(dispatch, {softwareProductId, version, componentId, formData}); } } else { @@ -96,12 +92,12 @@ const SoftwareProductComponentsMonitoringAction = { } }, - deleteSnmpFile(dispatch, {softwareProductId, componentId, type}){ + deleteSnmpFile(dispatch, {softwareProductId, version, componentId, type}){ if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - deleteSnmpTrapFile(dispatch, {softwareProductId, componentId}); + deleteSnmpTrapFile(dispatch, {softwareProductId, version, componentId}); } else { - deleteSnmpPollFile(dispatch, {softwareProductId, componentId}); + deleteSnmpPollFile(dispatch, {softwareProductId, version, componentId}); } } 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 index eeececb050..d908d36aaa 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ 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 index 72e0a85b10..54513b9634 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; export default (state = {}, action) => { 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 index ca090c5f2f..329cc70353 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React, {Component, PropTypes} from 'react'; import Dropzone from 'react-dropzone'; import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; @@ -46,7 +61,7 @@ class SoftwareProductComponentsMonitoringView extends Component { return ( <Dropzone className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`} - onDrop={files => this.handleImport(files, {isReadOnlyMode, type, refAndName})} + onDrop={(acceptedFiles, rejectedFiles) => this.handleImport(acceptedFiles, rejectedFiles, {isReadOnlyMode, type, refAndName})} onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} onDragLeave={() => this.setState({dragging:false})} multiple={false} @@ -70,7 +85,7 @@ class SoftwareProductComponentsMonitoringView extends Component { 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()}> + <div className='upload-btn primary-btn' data-test-id={`monitoring-upload-${refAndName}`} onClick={() => this.refs[refAndName].open()}> <span className='primary-btn-text'>{i18n('Select file')}</span> </div> </div> @@ -95,17 +110,21 @@ class SoftwareProductComponentsMonitoringView extends Component { } } - handleImport(files, {isReadOnlyMode, type, refAndName}) { + handleImport(files, rejectedFiles, {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); + if (files.length > 0) { + 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); + } else if (rejectedFiles.length > 0) { + this.setState({dragging: false}); + this.props.onFileUploadError(); + } } getFileTypeDisplayName(type) { 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 index a412456e13..7cf1f0189e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js @@ -1,53 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; import SoftwareProductComponentsNICEditorView from './SoftwareProductComponentsNICEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; let {network: {nicEditor = {}}} = softwareProductComponents; - let {data, qdata, qschema} = nicEditor; + let {data, qdata, genericFieldInfo, qgenericFieldInfo, dataMap, formReady} = nicEditor; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let protocols = []; + if(qdata && qdata.protocols && qdata.protocols.protocols && qdata.protocols.protocols.length){ + protocols = qdata.protocols.protocols; + } + let {version} = currentSoftwareProduct; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo) && ValidationHelper.checkFormValid(qgenericFieldInfo); return { currentSoftwareProduct, isValidityData, + version, data, qdata, - qschema, - isReadOnlyMode + dataMap, + isFormValid, + formReady, + genericFieldInfo, + qgenericFieldInfo, + isReadOnlyMode, + protocols }; }; const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { return { - onDataChanged: deltaData => SoftwareProductComponentsNetworkActionHelper.updateNICData(dispatch, {deltaData}), - onSubmit: ({data, qdata}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, + formName: forms.NIC_EDIT_FORM}), + onSubmit: ({data, qdata, version}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, version, componentId, data, qdata}), onCancel: () => SoftwareProductComponentsNetworkActionHelper.closeNICEditor(dispatch), - onQDataChanged: ({data}) => SoftwareProductComponentsNetworkActionHelper.updateNICQuestionnaire(dispatch, {data}) + onValidateForm: () => ValidationHelper.validateForm(dispatch, forms.NIC_EDIT_FORM), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: NIC_QUESTIONNAIRE}), }; }; 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 index d49f9ccb1e..b3c9fe5d98 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js @@ -1,48 +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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.NICEditor.OPEN: return { ...state, - data: action.nic + data: action.nic, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [] + } + }, + formName: forms.NIC_EDIT_FORM }; 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 index 7393a835dc..aad06c82f0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx @@ -1,321 +1,71 @@ +/*! + * 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. + */ 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 Form from 'nfvo-components/input/validation/Form.jsx'; +import Acceptable from './nicEditorComponents/Acceptable.jsx'; +import FlowLength from './nicEditorComponents/FlowLength.jsx'; +import OutFlowTraffic from './nicEditorComponents/OutFlowTraffic.jsx'; +import InFlowTraffic from './nicEditorComponents/InFlowTraffic.jsx'; +import Sizing from './nicEditorComponents/Sizing.jsx'; +import Network from './nicEditorComponents/Network.jsx'; +import IpConfig from './nicEditorComponents/IpConfig.jsx'; +import Protocols from './nicEditorComponents/Protocols.jsx'; +import NameAndPurpose from './nicEditorComponents/NameAndPurpose.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 {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, qgenericFieldInfo, dataMap, onDataChanged, protocols, onQDataChanged} = 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> + return ( + <div> + {qgenericFieldInfo && <Form + ref={(form) => { this.form = form; }} + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={isFormValid} + formReady={formReady} + onValidateForm={() => onValidateForm() } + className='vsp-components-network-editor'> + <div className='editor-data'> + <NameAndPurpose name={name} description={description} onDataChanged={onDataChanged} isReadOnlyMode={isReadOnlyMode}/> + <Protocols protocols={protocols} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <IpConfig dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <Network networkValues={netWorkValues} /> + <Sizing qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <InFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <OutFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <FlowLength qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <Acceptable qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> </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> + </Form> } </div> - ); } + submit() { - let {data, qdata, onSubmit} = this.props; - onSubmit({data, qdata}); + let {data, qdata, onSubmit, version} = this.props; + onSubmit({data, qdata, version}); } } 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 index bc53e1a7af..5cfc88bdc9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; export default (state = [], action) => { 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 index 8ff6b50189..bc061469b1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js @@ -1,65 +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 - * + * + * 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========================================================= + * 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. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; -function baseUrl(softwareProductId, componentId) { +function baseUrl(softwareProductId, version, componentId) { + const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics`; + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/nics`; } -function fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire${versionQuery}`); +function fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${nicId}/questionnaire`); } -function fetchNIC({softwareProductId, componentId, nicId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}${versionQuery}`); +function fetchNIC({softwareProductId, version, componentId, nicId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${nicId}`); } -function fetchNICsList({softwareProductId, componentId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +function fetchNICsList({softwareProductId, version, componentId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); } -function saveNIC({softwareProductId, componentId, nic: {id, name, description, networkId}}) { - return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${id}`,{ +function saveNIC({softwareProductId, version, componentId, nic: {id, name, description, networkId}}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${id}`,{ name, description, networkId }); } -function saveNICQuestionnaire({softwareProductId, componentId, nicId, qdata}) { - return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire`, qdata); +function saveNICQuestionnaire({softwareProductId, version, componentId, nicId, qdata}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${nicId}/questionnaire`, qdata); } const SoftwareProductComponentNetworkActionHelper = { - fetchNICsList(dispatch, {softwareProductId, componentId, version}) { - return fetchNICsList({softwareProductId, componentId, version}).then((response) => { + fetchNICsList(dispatch, {softwareProductId, version, componentId}) { + return fetchNICsList({softwareProductId, version, componentId}).then((response) => { dispatch({ type: actionTypes.NIC_LIST_UPDATE, response: response.results @@ -80,43 +75,24 @@ const SoftwareProductComponentNetworkActionHelper = { }); }, - 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) - } - }); - }); + loadNICData({softwareProductId, version, componentId, nicId}) { + return fetchNIC({softwareProductId, version, componentId, nicId}); }, - updateNICData(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.NICEditor.DATA_CHANGED, - deltaData - }); - }, - - updateNICQuestionnaire(dispatch, {data}) { - dispatch({ - type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, - payload: { - qdata: data - } + loadNICQuestionnaire(dispatch, {softwareProductId, version, componentId, nicId}) { + return fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}).then((response) => { + ValidationHelper.qDataLoaded(dispatch, {qName: NIC_QUESTIONNAIRE ,response: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + }}); }); }, - saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}) { + saveNICDataAndQuestionnaire(dispatch, {softwareProductId, version, componentId, data, qdata}) { SoftwareProductComponentNetworkActionHelper.closeNICEditor(dispatch); return Promise.all([ - saveNICQuestionnaire({softwareProductId, componentId, nicId: data.id, qdata}), - saveNIC({softwareProductId, componentId, nic: data}).then(() => { + saveNICQuestionnaire({softwareProductId, version, componentId, nicId: data.id, qdata}), + saveNIC({softwareProductId, version, componentId, nic: data}).then(() => { dispatch({ type: actionTypes.NIC_LIST_EDIT, nic: data 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 index 193f4b20b5..39c55d876c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -26,8 +21,8 @@ export const actionTypes = keyMirror({ NICEditor: { OPEN: null, - CLOSE: null, - NIC_QUESTIONNAIRE_UPDATE: null, - DATA_CHANGED: null + CLOSE: null } }); + +export const NIC_QUESTIONNAIRE = 'nic'; 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 index 9172dc691a..c2bd8ce479 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js @@ -1,51 +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 - * + * + * 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========================================================= + * 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. */ - 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'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; - let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, qschema}} = softwareProductComponents; + let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = 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, + dataMap, + qgenericFieldInfo, isValidityData, nicList, isDisplayModal: Boolean(data), isModalInEditMode, - manualMode, isReadOnlyMode }; @@ -53,28 +49,28 @@ export const mapStateToProps = ({softwareProduct}) => { const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onAddNIC: () => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: COMPONENTS_QUESTIONNAIRE}), onEditNicClick: (nic, version) => { Promise.all([ SoftwareProductComponentsNetworkActionHelper.loadNICData({ softwareProductId, + version, componentId, - nicId: nic.id, - version + nicId: nic.id }), SoftwareProductComponentsNetworkActionHelper.loadNICQuestionnaire(dispatch, { softwareProductId, + version, componentId, - nicId: nic.id, - version + nicId: nic.id }) ]).then( ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data}) ); }, - onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, - {softwareProductId, + onSubmit: ({qdata, version}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + {softwareProductId, version, vspComponentId: componentId, qdata}); } 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 index b3e17ff94b..f715016ba3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -1,10 +1,26 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import Form from 'nfvo-components/input/validation/Form.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 ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; +import Input from'nfvo-components/input/validation/Input.jsx'; import Modal from 'nfvo-components/modal/Modal.jsx'; import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js'; @@ -16,38 +32,56 @@ class SoftwareProductComponentsNetworkView extends React.Component { }; render() { - let {qdata, qschema, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; + let {dataMap, qgenericFieldInfo, 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}> +{ qgenericFieldInfo && <Form + formReady={null} + isValid={true} + onSubmit={() => this.save()} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> <h3 className='section-title'>{i18n('Network Capacity')}</h3> <div className='rows-section'> - <div className='row-flex-components input-row'> + <div className='row-flex-components'> <div className='single-col'> - <ValidationInput + <Input + data-test-id='protocolWithHighestTrafficProfileAcrossAllNICs' label={i18n('Protocol with Highest Traffic Profile across all NICs')} type='select' - pointer='/network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'/> + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'].isValid} + errorText={qgenericFieldInfo['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'].errorText} + value={dataMap['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + { qgenericFieldInfo['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'].enum.map(proto => + <option value={proto.enum} key={proto.enum}>{proto.title}</option>) } + </Input> </div> <div className='single-col add-line-break'> - <ValidationInput + <Input + data-test-id='networkTransactionsPerSecond' label={i18n('Network Transactions per Second')} - type='text' - pointer='/network/networkCapacity/networkTransactionsPerSecond'/> + type='number' + onChange={(ntps) => onQDataChanged({'network/networkCapacity/networkTransactionsPerSecond' : ntps})} + isValid={qgenericFieldInfo['network/networkCapacity/networkTransactionsPerSecond'].isValid} + errorText={qgenericFieldInfo['network/networkCapacity/networkTransactionsPerSecond'].errorText} + value={dataMap['network/networkCapacity/networkTransactionsPerSecond']} /> </div> <div className='empty-two-col' /> </div> </div> - </ValidationForm> + </Form> } </div> {this.renderNicList()} </div> @@ -70,18 +104,16 @@ class SoftwareProductComponentsNetworkView extends React.Component { renderNicList() { const {localFilter} = this.state; - let {onAddNIC, manualMode, isReadOnlyMode} = this.props; - let onAdd = manualMode ? onAddNIC : false; + let {isReadOnlyMode} = this.props; 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))} + onFilter={value => this.setState({localFilter: value})} + twoColumns> + {this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} </ListEditorView> ); } @@ -92,22 +124,22 @@ class SoftwareProductComponentsNetworkView extends React.Component { 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> + <ListEditorItemViewField> <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> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div className='details'> + <div className='title'>{i18n('Purpose of NIC')}</div> + <div className='description'>{description}</div> + </div> + <div className='details'> + <div className='title'>{i18n('Network')}</div> + <div className='artifact-name'>{networkName}</div> + </div> + </ListEditorItemViewField> </ListEditorItemView> ); @@ -128,8 +160,8 @@ class SoftwareProductComponentsNetworkView extends React.Component { } save() { - let {onSubmit, qdata} = this.props; - return onSubmit({qdata}); + let {onSubmit, qdata, version} = this.props; + return onSubmit({qdata, version}); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Acceptable.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Acceptable.jsx new file mode 100644 index 0000000000..524b95c3ad --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Acceptable.jsx @@ -0,0 +1,75 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Acceptable = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection> + <GridItem colSpan={3}> + <div className='part-title-small packets'>{i18n('Acceptable Jitter')}</div> + </GridItem> + <GridItem> + <div className='part-title-small bytes'>{i18n('Allow Packet Loss')}</div> + </GridItem> + <GridItem> + <Input + label={i18n('Mean')} + type='number' + data-test-id='acceptableJitter-mean' + isValid={qgenericFieldInfo['sizing/acceptableJitter/mean'].isValid} + errorText={qgenericFieldInfo['sizing/acceptableJitter/mean'].errorText} + value={dataMap['sizing/acceptableJitter/mean']} + onChange={val => onQDataChanged({'sizing/acceptableJitter/mean' : val})} /> + </GridItem> + <GridItem> + <Input + label={i18n('Max')} + type='number' + data-test-id='acceptableJitter-max' + isValid={qgenericFieldInfo['sizing/acceptableJitter/max'].isValid} + errorText={qgenericFieldInfo['sizing/acceptableJitter/max'].errorText} + value={dataMap['sizing/acceptableJitter/max']} + onChange={val => onQDataChanged({'sizing/acceptableJitter/max' : val})} /> + </GridItem> + <GridItem> + <Input + label={i18n('Var')} + type='number' + data-test-id='acceptableJitter-variable' + isValid={qgenericFieldInfo['sizing/acceptableJitter/variable'].isValid} + errorText={qgenericFieldInfo['sizing/acceptableJitter/variable'].errorText} + value={dataMap['sizing/acceptableJitter/variable']} + onChange={val => onQDataChanged({'sizing/acceptableJitter/variable' : val})} /> + </GridItem> + <GridItem> + <Input + label={i18n('In Percent')} + type='number' + data-test-id='acceptableJitter-acceptablePacketLoss' + isValid={qgenericFieldInfo['sizing/acceptablePacketLoss'].isValid} + errorText={qgenericFieldInfo['sizing/acceptablePacketLoss'].errorText} + value={dataMap['sizing/acceptablePacketLoss']} + onChange={val => onQDataChanged({'sizing/acceptablePacketLoss' : val})} /> + </GridItem> + </GridSection> + ); +}; + +export default Acceptable; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/FlowLength.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/FlowLength.jsx new file mode 100644 index 0000000000..3745fc7c2e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/FlowLength.jsx @@ -0,0 +1,35 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import PacketsBytes from './PacketsBytes.jsx'; + +const pointers = [ + {label: 'Peak', value: 'sizing/flowLength/packets/peak'}, + {label: 'Avg', value: 'sizing/flowLength/packets/avg'}, + {label: 'Peak', value: 'sizing/flowLength/bytes/peak'}, + {label: 'Avg', value: 'sizing/flowLength/bytes/avg'}, +]; + +const FlowLength = (props) => { + return( + <PacketsBytes {...props} title={i18n('Flow Length')} pointers={pointers}/> + ); +}; + +export default FlowLength; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/InFlowTraffic.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/InFlowTraffic.jsx new file mode 100644 index 0000000000..5476ed90e6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/InFlowTraffic.jsx @@ -0,0 +1,35 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import PacketsBytes from './PacketsBytes.jsx'; + +const pointers = [ + {label: 'Peak', value: 'sizing/inflowTrafficPerSecond/packets/peak'}, + {label: 'Avg', value: 'sizing/inflowTrafficPerSecond/packets/avg'}, + {label: 'Peak', value: 'sizing/inflowTrafficPerSecond/bytes/peak'}, + {label: 'Avg', value: 'sizing/inflowTrafficPerSecond/bytes/avg'}, +]; + +const InFlowTraffic = (props) => { + return( + <PacketsBytes {...props} title={i18n('Inflow Traffic per second')} pointers={pointers}/> + ); +}; + +export default InFlowTraffic; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/IpConfig.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/IpConfig.jsx new file mode 100644 index 0000000000..b3a5d21625 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/IpConfig.jsx @@ -0,0 +1,45 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const IpConfig = ({dataMap, onQDataChanged}) => { + return ( + <GridSection title={i18n('IP Configuration')}> + <GridItem> + <Input + label={i18n('IPv4 Required')} + type='checkbox' + onChange={value => onQDataChanged({'ipConfiguration/ipv4Required' : value})} + data-test-id='ipConfiguration-ipv4Required' + value={dataMap['ipConfiguration/ipv4Required']} /> + </GridItem> + <GridItem> + <Input + label={i18n('IPv6 Required')} + type='checkbox' + data-test-id='ipConfiguration-ipv6Required' + onChange={value => onQDataChanged({'ipConfiguration/ipv6Required' : value})} + value={dataMap['ipConfiguration/ipv6Required']} /> + </GridItem> + </GridSection> + ); +}; + +export default IpConfig; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx new file mode 100644 index 0000000000..3dc153d27f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx @@ -0,0 +1,54 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const NameAndPurpose = ({onDataChanged, isReadOnlyMode, name, description}) => { + + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + label={i18n('Name')} + value={name} + data-test-id='nic-name' + disabled={true} + type='text' /> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Purpose of NIC')} + value={description} + data-test-id='nic-description' + onChange={description => onDataChanged({description})} + disabled={isReadOnlyMode} + type='textarea'/> + </GridItem> + </GridSection> + ); +}; + +NameAndPurpose.PropTypes = { + name: React.PropTypes.string, + description: React.PropTypes.array, + onDataChanged: React.PropTypes.func, + isReadOnlyMode: React.PropTypes.bool, +}; + +export default NameAndPurpose; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx new file mode 100644 index 0000000000..43afdbed3a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx @@ -0,0 +1,62 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Network = ({networkValues}) => { + return ( + <GridSection title={i18n('Network')}> + <GridItem> + <Input + label={i18n('Internal')} + disabled + checked={true} + data-test-id='nic-internal' + className='network-radio disabled' + type='radio'/> + </GridItem> + <GridItem> + <Input + label={i18n('External')} + disabled + checked={false} + data-test-id='nic-external' + className='network-radio disabled' + type='radio'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Network')} + data-test-id='nic-network' + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + disabled={true} > + {networkValues.map(val => <option key={val.enum} value={val.enum}>{val.title}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +Network.PropTypes = { + networkValues: React.PropTypes.array +}; + +export default Network; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/OutFlowTraffic.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/OutFlowTraffic.jsx new file mode 100644 index 0000000000..80a3d1579b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/OutFlowTraffic.jsx @@ -0,0 +1,35 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import PacketsBytes from './PacketsBytes.jsx'; + +const pointers = [ + {label: 'Peak', value: 'sizing/outflowTrafficPerSecond/packets/peak'}, + {label: 'Avg', value: 'sizing/outflowTrafficPerSecond/packets/avg'}, + {label: 'Peak', value: 'sizing/outflowTrafficPerSecond/bytes/peak'}, + {label: 'Avg', value: 'sizing/outflowTrafficPerSecond/bytes/avg'}, +]; + +const OutFlowTraffic = (props) => { + return( + <PacketsBytes {...props} title={i18n('Outflow Traffic per second')} pointers={pointers}/> + ); +}; + +export default OutFlowTraffic; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/PacketsBytes.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/PacketsBytes.jsx new file mode 100644 index 0000000000..d7ee91bd15 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/PacketsBytes.jsx @@ -0,0 +1,65 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const PointerInput = ({label, value, onQDataChanged, qgenericFieldInfo, dataMap}) => { + return ( + <GridItem> + <Input + label={i18n(label)} + type='number' + data-test-id={`${value}`} + isValid={qgenericFieldInfo[value].isValid} + errorText={qgenericFieldInfo[value].errorText} + value={dataMap[value]} + onChange={val => onQDataChanged({[value]: val})} /> + </GridItem> + ); +}; + +PointerInput.PropTypes = { + label: React.PropTypes.string, + value: React.PropTypes.string +}; + +const PacketsBytes = ({title, pointers = [], qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={title}> + <GridItem colSpan={2}> + <div className='part-title-small packets'>{i18n('Packets')}</div> + </GridItem> + <GridItem colSpan={2}> + <div className='part-title-small bytes'>{i18n('Bytes')}</div> + </GridItem> + {pointers.map(pointer => {return (<PointerInput key={pointer.value} label={pointer.label} value={pointer.value} + qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged} dataMap={dataMap} />);})} + </GridSection> + ); +}; + +PacketsBytes.PropTypes = { + title: React.PropTypes.string, + pointers: React.PropTypes.array, + onQDataChanged: React.PropTypes.function, + dataMap: React.PropTypes.object, + qgenericFieldInfo: React.PropTypes.object +}; + +export default PacketsBytes; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Protocols.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Protocols.jsx new file mode 100644 index 0000000000..3e8a9f4e77 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Protocols.jsx @@ -0,0 +1,74 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Protocols = ({protocols, qgenericFieldInfo, dataMap, onQDataChanged}) => { + return ( + <GridSection title={i18n('Protocols')}> + <GridItem colSpan={2}> + <InputOptions + data-test-id='nic-protocols' + label={i18n('Protocols')} + type='select' + isMultiSelect={true} + isValid={qgenericFieldInfo['protocols/protocols'].isValid} + errorText={qgenericFieldInfo['protocols/protocols'].errorText} + onInputChange={()=>{}} + onEnumChange={protocols => { + onQDataChanged({'protocols/protocols' : protocols});} + } + multiSelectedEnum={dataMap['protocols/protocols']} + clearable={false} + values={qgenericFieldInfo['protocols/protocols'].enum}/> + </GridItem> + <GridItem colSpan={2}> + <Input + data-test-id='nic-protocolWithHighestTrafficProfile' + label={i18n('Protocol with Highest Traffic Profile')} + type='select' + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['protocols/protocolWithHighestTrafficProfile'].isValid} + errorText={qgenericFieldInfo['protocols/protocolWithHighestTrafficProfile'].errorText} + value={dataMap['protocols/protocolWithHighestTrafficProfile']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'protocols/protocolWithHighestTrafficProfile' : val});} + }> + {(protocols.length === 0) && + <option key={'You must select protocols first...'} value=''>{i18n('You must select protocols first...')}</option> + } + {protocols.map(protocol => <option key={protocol} value={protocol}>{protocol}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +Protocols.PropTypes = { + protocols: React.PropTypes.array, + onQDataChanged: React.PropTypes.function, + dataMap: React.PropTypes.object, + qgenericFieldInfo: React.PropTypes.object +}; + +export default Protocols; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Sizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Sizing.jsx new file mode 100644 index 0000000000..1dd0045f7b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Sizing.jsx @@ -0,0 +1,39 @@ +/*! + * 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. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Sizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={i18n('Sizing')}> + <GridItem colSpan={4}> + <Input + label={i18n('Describe Quality of Service')} + type='textarea' + data-test-id='sizing-describeQualityOfService' + isValid={qgenericFieldInfo['sizing/describeQualityOfService'].isValid} + errorText={qgenericFieldInfo['sizing/describeQualityOfService'].errorText} + value={dataMap['sizing/describeQualityOfService']} + onChange={val => onQDataChanged({'sizing/describeQualityOfService' : val}) }/> + </GridItem> + </GridSection> + ); +}; + +export default Sizing; 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 index d535a34a82..b2133ad5d8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js @@ -1,69 +1,65 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; -function baseUrl(softwareProductId, componentId) { +function baseUrl(softwareProductId, version, componentId) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`; + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${version.id}/components/${componentId}/processes`; } -function fetchProcessesList({softwareProductId, componentId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +function fetchProcessesList({softwareProductId, version, componentId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); } -function deleteProcess({softwareProductId, componentId, processId}) { - return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId)}/${processId}`); +function deleteProcess({softwareProductId, version, componentId, processId}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/${processId}`); } -function putProcess({softwareProductId, componentId, process}) { - return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${process.id}`, { +function putProcess({softwareProductId, version, componentId, process}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${process.id}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function postProcess({softwareProductId,componentId, process}) { - return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}`, { +function postProcess({softwareProductId, version, componentId, process}) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version, componentId)}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function uploadFileToProcess({softwareProductId, processId, componentId, formData}) { - return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}/${processId}/upload`, formData); +function uploadFileToProcess({softwareProductId, version, processId, componentId, formData}) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version, componentId)}/${processId}/upload`, formData); } const SoftwareProductComponentProcessesActionHelper = { - fetchProcessesList(dispatch, {softwareProductId, componentId, version}) { + fetchProcessesList(dispatch, {softwareProductId, version, componentId}) { dispatch({ type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, processesList: [] }); - return fetchProcessesList({softwareProductId, componentId, version}).then(response => { + return fetchProcessesList({softwareProductId, version, componentId}).then(response => { dispatch({ type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, processesList: response.results @@ -71,8 +67,8 @@ const SoftwareProductComponentProcessesActionHelper = { }); }, - deleteProcess(dispatch, {process, softwareProductId, componentId}) { - return deleteProcess({softwareProductId, processId:process.id, componentId}).then(() => { + deleteProcess(dispatch, {process, softwareProductId, version, componentId}) { + return deleteProcess({softwareProductId, version, processId:process.id, componentId}).then(() => { dispatch({ type: actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, processId: process.id @@ -81,11 +77,11 @@ const SoftwareProductComponentProcessesActionHelper = { }, - saveProcess(dispatch, {softwareProductId, componentId, previousProcess, process}) { + saveProcess(dispatch, {softwareProductId, version, componentId, previousProcess, process}) { if (previousProcess) { - return putProcess({softwareProductId,componentId, process}).then(() => { + return putProcess({softwareProductId, version, componentId, process}).then(() => { if (process.formData && process.formData.name !== previousProcess.artifactName){ - uploadFileToProcess({softwareProductId, processId: process.id, formData: process.formData, componentId}); + uploadFileToProcess({softwareProductId, version, processId: process.id, formData: process.formData, componentId}); } dispatch({ type: actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, @@ -94,9 +90,9 @@ const SoftwareProductComponentProcessesActionHelper = { }); } else { - return postProcess({softwareProductId, componentId, process}).then(response => { + return postProcess({softwareProductId, version, componentId, process}).then(response => { if (process.formData) { - uploadFileToProcess({softwareProductId, processId: response.value, formData: process.formData, componentId}); + uploadFileToProcess({softwareProductId, version, processId: response.value, formData: process.formData, componentId}); } dispatch({ type: actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, @@ -133,12 +129,6 @@ const SoftwareProductComponentProcessesActionHelper = { dispatch({ type:actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE }); - }, - processEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.processEditor.DATA_CHANGED, - deltaData - }); } }; 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 index 78a111a426..d15432b3fb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -27,8 +22,15 @@ export const actionTypes = keyMirror({ 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 - } + SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM: null }); + +export const optionsInputValues = { + PROCESS_TYPE: [ + {title: 'Select...', enum: ''}, + {title: 'Lifecycle Operations', enum: 'Lifecycle_Operations'}, + {title: 'Other', enum: 'Other'} + ] +}; + +export const SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM = 'SOFTWAREPRODUCTPROCESSCOMPONENTSEDITORFORM'; 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 index 0138023c30..9502e24b1a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js @@ -1,31 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper'; import SoftwareProductComponentProcessesEditorView from './SoftwareProductComponentProcessesEditorView.jsx'; +import {SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM} from './SoftwareProductComponentProcessesConstants.js'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductComponents: {componentProcesses = {}}} = softwareProduct; let {processesList = [], processesEditor = {}} = componentProcesses; - let {data} = processesEditor; + let {data, genericFieldInfo, formReady} = processesEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); let previousData; const processId = data ? data.id : null; @@ -35,19 +33,23 @@ const mapStateToProps = ({softwareProduct}) => { return { data, - previousData + genericFieldInfo, + previousData, + isFormValid, + formReady }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onDataChanged: deltaData => SoftwareProductComponentProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM}), onCancel: () => SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch), onSubmit: ({previousProcess, process}) => { SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch); - SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, componentId, process}); - } + SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, version, previousProcess, componentId, process}); + }, + onValidateForm: () => ValidationHelper.validateForm(dispatch, SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM) }; }; 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 index f859f690e8..9afaa6d5fd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js @@ -1,43 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; +import {actionTypes, SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM} from './SoftwareProductComponentProcessesConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: return { ...state, + formReady: null, + formName: SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'artifactName' : { + isValid: true, + errorText: '', + validations: [] + }, + 'type' : { + isValid: true, + errorText: '', + validations: [] + } + }, 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 index ca6d843af7..18f2ee129c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx @@ -1,18 +1,48 @@ +/*! + * 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. + */ 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'; +import {optionsInputValues as ComponentProcessesOptionsInputValues} from './SoftwareProductComponentProcessesConstants.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, - artifactName: React.PropTypes.string + artifactName: React.PropTypes.string, + type: React.PropTypes.string }); +const FileUploadBox = ({onClick}) => { + return ( + <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={onClick}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + ); +}; + class SoftwareProductProcessesEditorView extends React.Component { state = { @@ -30,65 +60,91 @@ class SoftwareProductProcessesEditorView extends React.Component { }; render() { - let {isReadOnlyMode, onCancel, onDataChanged, data = {}} = this.props; - let {name, description, artifactName} = data; + let {isReadOnlyMode, onCancel, onDataChanged, genericFieldInfo, data = {}} = this.props; + let {name, description, artifactName, type} = data; return ( <div> - <ValidationForm + { genericFieldInfo && <Form ref='validationForm' isReadOnlyMode={isReadOnlyMode} hasButtons={true} labledButtons={true} onSubmit={ () => this.submit() } onReset={ () => onCancel() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm() } 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 className={`vsp-processes-editor-data${isReadOnlyMode ? ' disabled' : '' }`}> + <Dropzone + className={`vsp-process-dropzone-view ${this.state.dragging ? 'active-dragging' : ''}`} + onDrop={(acceptedFiles, rejectedFiles) => this.handleImportSubmit(acceptedFiles, rejectedFiles)} + onDragEnter={() => this.setState({dragging:true})} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput'> + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo.name.isValid} + isRequired={true} + data-test-id='name' + errorText={genericFieldInfo.name.errorText} + label={i18n('Name')} + value={name} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <FileUploadBox onClick={() => this.refs.processEditorFileInput.open()} /> + </GridItem> + </GridSection> + <GridSection> + <GridItem colSpan={2}> + <Input + name='vsp-process-description' + groupClassName='vsp-process-description' + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + label={i18n('Notes')} + value={description} + data-test-id='vsp-process-description' + type='textarea'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Artifacts')} + data-test-id='artifacts' + value={artifactName} + type='text' + disabled/> + <Input + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({type: val});} + } + value={type} + label={i18n('Process Type')} + data-test-id='process-type' + isValid={genericFieldInfo.type.isValid} + errorText={genericFieldInfo.type.errorText} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' > + {ComponentProcessesOptionsInputValues.PROCESS_TYPE.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + </GridSection> + </Dropzone> + </div> + </Form>} </div> ); } @@ -110,14 +166,25 @@ class SoftwareProductProcessesEditorView extends React.Component { } - handleImportSubmit(files) { - let {onDataChanged} = this.props; - this.setState({ - dragging: false, - complete: '0', - files - }); - onDataChanged({artifactName: files[0].name}); + handleImportSubmit(files, rejectedFiles) { + if (files.length > 0) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } + else if (rejectedFiles.length > 0) { + this.setState({ + dragging: false + }); + if (DEBUG) { + console.log('file was rejected ' + rejectedFiles[0].name); + } + } + } } 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 index 5f6932897e..a8cb709194 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js @@ -1,30 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; import SoftwareProductComponentsProcessesListView from './SoftwareProductComponentsProcessesListView.jsx'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents: {componentProcesses = {}}} = softwareProduct; let{processesList = [], processesEditor = {}} = componentProcesses; @@ -42,12 +39,19 @@ const mapStateToProps = ({softwareProduct}) => { }; -const mapActionsToProps = (dispatch, {softwareProductId}) => { +const mapActionsToProps = (dispatch, {componentId, softwareProductId}) => { return { onAddProcess: () => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch), onEditProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch, process), - onDeleteProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + onDeleteProcessClick: (process, version) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + onConfirmed: ()=> SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, + {process, softwareProductId, version, componentId}) + } + }) }; }; 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 index 4bb124d52f..98e24a9c21 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; export default (state = [], action) => { 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 deleted file mode 100644 index 48fa862364..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx +++ /dev/null @@ -1,45 +0,0 @@ -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 index a8b07e9194..650d6d5ebc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Modal from 'nfvo-components/modal/Modal.jsx'; @@ -6,7 +21,6 @@ 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 { @@ -22,12 +36,11 @@ class SoftwareProductProcessesView extends React.Component { isModalInEditMode: React.PropTypes.bool, onStorageSelect: React.PropTypes.func, componentId: React.PropTypes.string, - softwareProductId: React.PropTypes.string + softwareProductId: React.PropTypes.string, + currentSoftwareProduct: React.PropTypes.object }; render() { - let { softwareProductId, componentId} = this.props; - return ( <div className='vsp-processes-page'> <div className='software-product-view'> @@ -35,18 +48,15 @@ class SoftwareProductProcessesView extends React.Component { {this.renderEditor()} {this.renderProcessList()} </div> - <SoftwareProductComponentsProcessesConfirmationModal - componentId={componentId} - softwareProductId={softwareProductId}/> </div> </div> ); } renderEditor() { - let {softwareProductId, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {softwareProductId, currentSoftwareProduct: {version}, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; return ( - <Modal show={isDisplayModal} bsSize='large' animation={true}> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal'> <Modal.Header> <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title> </Modal.Header> @@ -54,6 +64,7 @@ class SoftwareProductProcessesView extends React.Component { <SoftwareProductProcessesEditor componentId={componentId} softwareProductId={softwareProductId} + version={version} isReadOnlyMode={isReadOnlyMode}/> </Modal.Body> </Modal> @@ -72,7 +83,8 @@ class SoftwareProductProcessesView extends React.Component { placeholder={i18n('Filter Process')} onAdd={onAddProcess} isReadOnlyMode={isReadOnlyMode} - onFilter={filter => this.setState({localFilter: filter})}> + title={i18n('Process Details')} + onFilter={value => this.setState({localFilter: value})}> {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} </ListEditorView> </div> @@ -81,14 +93,14 @@ class SoftwareProductProcessesView extends React.Component { renderProcessListItem(process, isReadOnlyMode) { let {id, name, description, artifactName = ''} = process; - let {onEditProcessClick, onDeleteProcessClick} = this.props; + let {currentSoftwareProduct: {version}, onEditProcessClick, onDeleteProcessClick} = this.props; return ( <ListEditorItemView key={id} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode} onSelect={() => onEditProcessClick(process)} - onDelete={() => onDeleteProcessClick(process)}> + onDelete={() => onDeleteProcessClick(process, version)}> <div className='list-editor-item-view-field'> <div className='title'>{i18n('Name')}</div> 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 index fbd3f81ec2..18a3b1e8ff 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js @@ -1,47 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductComponentStorageView from './SoftwareProductComponentStorageView.jsx'; +import {COMPONENTS_QUESTIONNAIRE} from '../SoftwareProductComponentsConstants.js'; + const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {data: componentData , qdata, qschema}} = softwareProductComponents; + let {componentEditor: {data: componentData , qdata, qgenericFieldInfo : qGenericFieldInfo, dataMap}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { componentData, qdata, - qschema, - isReadOnlyMode + isReadOnlyMode, + qGenericFieldInfo, + dataMap }; }; -const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), + onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + {softwareProductId, version, vspComponentId: componentId, componentData, qdata}); + } }; }; 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 index 9c9600c376..28bdf8e5e5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx @@ -1,8 +1,156 @@ +/*! + * 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. + */ 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 Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import classnames from 'classnames'; +const BackupSection = ({isReadOnlyMode,dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Backup')}> + <GridItem> + <div className='vertical-flex'> + <label key='label' className={classnames('control-label',{'disabled': isReadOnlyMode})}>{i18n('Backup Type')}</label> + <div className='radio-options-content-row'> + {qgenericFieldInfo['storage/backup/backupType'].enum.map(onSite => ( + <Input + data-test-id='backupType' + type='radio' + key={onSite.enum} + name={'compute/guestOS/bitSize'} + className='radio-field' + value={onSite.enum} + label={onSite.title} + onChange={(site) => onQDataChanged({'storage/backup/backupType' : site})} + isValid={qgenericFieldInfo['storage/backup/backupType'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupType'].errorText} + checked={dataMap['storage/backup/backupType'] === onSite.enum} /> )) } + </div> + </div> + </GridItem> + <GridItem> + <Input + className='section-field' + data-test-id='backupSolution' + onChange={(backupSolution) => onQDataChanged({'storage/backup/backupSolution' : backupSolution})} + label={i18n('Backup Solution')} + type='text' + isValid={qgenericFieldInfo['storage/backup/backupSolution'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupSolution'].errorText} + value={dataMap['storage/backup/backupSolution']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + data-test-id='backupStorageSize' + onChange={(backupStorageSize) => onQDataChanged({'storage/backup/backupStorageSize' : backupStorageSize})} + label={i18n('Backup Storage Size (GB)')} + type='number' + isValid={qgenericFieldInfo['storage/backup/backupStorageSize'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupStorageSize'].errorText} + value={dataMap['storage/backup/backupStorageSize']}/> + </GridItem> + <GridItem> + <Input + data-test-id='backupNIC' + label={i18n('Backup NIC')} + type='select' + className='input-options-select section-field' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['storage/backup/backupNIC'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupNIC'].errorText} + value={dataMap['storage/backup/backupNIC']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'storage/backup/backupNIC' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['storage/backup/backupNIC'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + </GridSection> +); + +const SnapshotBackupSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Snapshot Backup')}> + <GridItem> + <Input + className='section-field' + data-test-id='snapshotFrequency' + onChange={(snapshotFrequency) => onQDataChanged({'storage/snapshotBackup/snapshotFrequency' : snapshotFrequency})} + label={i18n('Backup Storage Size (GB)')} + type='number' + isValid={qgenericFieldInfo['storage/snapshotBackup/snapshotFrequency'].isValid} + errorText={qgenericFieldInfo['storage/snapshotBackup/snapshotFrequency'].errorText} + value={dataMap['storage/snapshotBackup/snapshotFrequency']}/> + </GridItem> + </GridSection> +); + +const LogBackupSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Log Backup')}> + <GridItem> + <Input + className='section-field' + data-test-id='sizeOfLogFiles' + onChange={(sizeOfLogFiles) => onQDataChanged({'storage/logBackup/sizeOfLogFiles' : sizeOfLogFiles})} + label={i18n('Backup Storage Size (GB)')} + type='number' + isValid={qgenericFieldInfo['storage/logBackup/sizeOfLogFiles'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/sizeOfLogFiles'].errorText} + value={dataMap['storage/logBackup/sizeOfLogFiles']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + label={i18n('Log Retention Period (days)')} + data-test-id='logRetentionPeriod' + onChange={(logRetentionPeriod) => onQDataChanged({'storage/logBackup/logRetentionPeriod' : logRetentionPeriod})} + type='number' + isValid={qgenericFieldInfo['storage/logBackup/logRetentionPeriod'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/logRetentionPeriod'].errorText} + value={dataMap['storage/logBackup/logRetentionPeriod']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + label={i18n('Log Backup Frequency (days)')} + data-test-id='logBackupFrequency' + onChange={(logBackupFrequency) => onQDataChanged({'storage/logBackup/logBackupFrequency' : logBackupFrequency})} + type='number' + isValid={qgenericFieldInfo['storage/logBackup/logBackupFrequency'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/logBackupFrequency'].errorText} + value={dataMap['storage/logBackup/logBackupFrequency']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + label={i18n('Log File Location')} + data-test-id='logFileLocation' + onChange={(logFileLocation) => onQDataChanged({'storage/logBackup/logFileLocation' : logFileLocation})} + type='text' + isValid={qgenericFieldInfo['storage/logBackup/logFileLocation'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/logFileLocation'].errorText} + value={dataMap['storage/logBackup/logFileLocation']}/> + </GridItem> + </GridSection> +); class SoftwareProductComponentStorageView extends React.Component { @@ -14,110 +162,28 @@ class SoftwareProductComponentStorageView extends React.Component { }; render() { - let {qdata, qschema, onQDataChanged, onSubmit, isReadOnlyMode} = this.props; + let {onQDataChanged, dataMap, qGenericFieldInfo, isReadOnlyMode, onSubmit, qdata} = this.props; return( <div className='vsp-component-questionnaire-view'> - <ValidationForm - ref='storageValidationForm' - hasButtons={false} + {qGenericFieldInfo && <Form + ref={form => this.form = form } + isValid={true} + formReady={null} 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> + hasButtons={false}> + <BackupSection isReadOnlyMode={isReadOnlyMode} onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <SnapshotBackupSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <LogBackupSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + </Form> } </div> ); } save(){ - return this.refs.storageValidationForm.handleFormSubmit(new Event('dummy')); + return this.form.handleFormSubmit(new Event('dummy')); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js index 46308f0045..19e2d5b0db 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js @@ -1,48 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; import SoftwareProductCreationActionHelper from './SoftwareProductCreationActionHelper.js'; import SoftwareProductCreationView from './SoftwareProductCreationView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductActionHelper from '../SoftwareProductActionHelper.js'; + +export const mapStateToProps = ({finalizedLicenseModelList, softwareProductList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => { + let {genericFieldInfo} = softwareProductCreation; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + let VSPNames = {}; + for (let i = 0; i < softwareProductList.length; i++) { + VSPNames[softwareProductList[i].name] = softwareProductList[i].id; + } -const mapStateToProps = ({finalizedLicenseModelList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => { return { data: softwareProductCreation.data, + selectedVendorId: softwareProductCreation.selectedVendorId, + disableVendor: softwareProductCreation.disableVendor, softwareProductCategories, - finalizedLicenseModelList + finalizedLicenseModelList, + isFormValid, + formReady: softwareProductCreation.formReady, + genericFieldInfo, + VSPNames }; }; -const mapActionsToProps = (dispatch) => { +export const mapActionsToProps = (dispatch) => { return { - onDataChanged: deltaData => SoftwareProductCreationActionHelper.changeData(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), 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}); + SoftwareProductCreationActionHelper.createSoftwareProduct(dispatch, {softwareProduct}).then(response => { + SoftwareProductActionHelper.fetchSoftwareProductList(dispatch).then(() => { + let {vendorId: licenseModelId, licensingVersion} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId: response.vspId, licenseModelId, licensingVersion}); + }); }); - } + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js index f4e51f198e..3b434e3ba4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -1,29 +1,26 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - 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 as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import {actionTypes} from './SoftwareProductCreationConstants.js'; - +import i18n from 'nfvo-utils/i18n/i18n.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -31,7 +28,7 @@ function baseUrl() { } function createSoftwareProduct(softwareProduct) { - return RestAPIUtil.create(baseUrl(), { + return RestAPIUtil.post(baseUrl(), { ...softwareProduct, icon: 'icon', licensingData: {} @@ -40,36 +37,39 @@ function createSoftwareProduct(softwareProduct) { const SoftwareProductCreationActionHelper = { - open(dispatch) { + open(dispatch, vendorId) { SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); dispatch({ - type: actionTypes.OPEN + type: actionTypes.OPEN, + selectedVendorId: vendorId }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.SOFTWARE_PRODUCT_CREATION, + title: i18n('New Software Product'), + modalComponentProps: { + vendorId + } + } + }); + }, resetData(dispatch) { + dispatch({ - type: actionTypes.RESET_DATA + type: modalActionTypes.GLOBAL_MODAL_CLOSE }); - }, - changeData(dispatch, {deltaData}) { dispatch({ - type: actionTypes.DATA_CHANGED, - deltaData + type: actionTypes.RESET_DATA }); }, createSoftwareProduct(dispatch, {softwareProduct}) { - return createSoftwareProduct(softwareProduct).then(response => { - SoftwareProductActionHelper.addSoftwareProduct(dispatch, { - softwareProduct: { - ...softwareProduct, - id: response.vspId - } - }); - return response.vspId; - }); + return createSoftwareProduct(softwareProduct); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js index 0a9cdb911c..241d7985b1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js @@ -1,27 +1,23 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ OPEN: null, - RESET_DATA: null, - DATA_CHANGED: null + RESET_DATA: null }); + +export const SP_CREATION_FORM_NAME = 'SPCREATIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js index 5e3db09e56..f7a738518e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -1,40 +1,58 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from './SoftwareProductCreationConstants.js'; +import {actionTypes, SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.OPEN: return { ...state, - data: {}, - showModal: true - }; - case actionTypes.DATA_CHANGED: - return { - ...state, + formName: SP_CREATION_FORM_NAME, + disableVendor: action.selectedVendorId ? true : false, data: { - ...state.data, - ...action.deltaData - } + vendorId: action.selectedVendorId ? action.selectedVendorId : undefined + }, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'freeEnglishText', data: true}, {type: 'maxLength', data: 1000}, {type: 'required', data: true}] + }, + 'vendorId' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'subCategory' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'category' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}, {type: 'validateName', data: true}] + } + }, + showModal: true }; case actionTypes.RESET_DATA: return {}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx index 2c8f243457..11b696855b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx @@ -1,11 +1,28 @@ +/*! + * 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. + */ 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 Validator from 'nfvo-utils/Validator.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import {SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; +import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; - const SoftwareProductPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, @@ -21,50 +38,66 @@ class SoftwareProductCreationView extends React.Component { data: SoftwareProductPropType, finalizedLicenseModelList: React.PropTypes.array, softwareProductCategories: React.PropTypes.array, + VSPNames: React.PropTypes.object, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, onCancel: React.PropTypes.func.isRequired }; render() { - let {softwareProductCategories, data = {}, onDataChanged, onCancel} = this.props; + let {softwareProductCategories, data = {}, onDataChanged, onCancel, genericFieldInfo, disableVendor} = this.props; let {name, description, vendorId, subCategory} = data; const vendorList = this.getVendorList(); - return ( <div className='software-product-creation-page'> - <ValidationForm - ref='validationForm' + { genericFieldInfo && <Form + ref={(validationForm) => this.validationForm = validationForm} hasButtons={true} onSubmit={() => this.submit() } onReset={() => onCancel() } - labledButtons={true}> + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.validate() }> <div className='software-product-form-row'> <div className='software-product-inline-section'> - <ValidationInput + <Input value={name} label={i18n('Name')} - ref='software-product-name' - onChange={name => onDataChanged({name})} - validations={{validateName: true, maxLength: 25, required: true}} + isRequired={true} + onChange={name => onDataChanged({name},SP_CREATION_FORM_NAME, {name: name => this.validateName(name)})} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} type='text' - className='field-section'/> - <ValidationInput - onEnumChange={vendorId => onDataChanged({vendorId})} - value={vendorId} + className='field-section' + data-test-id='new-vsp-name' /> + <Input label={i18n('Vendor')} - values={vendorList} - validations={{required: true}} type='select' - className='field-section'/> - <ValidationInput + value={vendorId} + isRequired={true} + disabled={disableVendor} + onChange={e => this.onSelectVendor(e)} + isValid={genericFieldInfo.vendorId.isValid} + errorText={genericFieldInfo.vendorId.errorText} + className='input-options-select' + groupClassName='bootstrap-input-options' + data-test-id='new-vsp-vendor' > + {vendorList.map(vendor => + <option key={vendor.title} value={vendor.enum}>{vendor.title}</option>)} + </Input> + <Input label={i18n('Category')} type='select' value={subCategory} - onChange={subCategory => this.onSelectSubCategory(subCategory)} - validations={{required: true}} - className='options-input-category'> + isRequired={true} + onChange={e => this.onSelectSubCategory(e)} + isValid={genericFieldInfo.subCategory.isValid} + errorText={genericFieldInfo.subCategory.errorText} + className='input-options-select' + groupClassName='bootstrap-input-options' + data-test-id='new-vsp-category' > <option key='' value=''>{i18n('please select…')}</option> {softwareProductCategories.map(category => category.subcategories && @@ -74,20 +107,23 @@ class SoftwareProductCreationView extends React.Component { <option key={sub.uniqueId} value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)} </optgroup>) } - </ValidationInput> + </Input> </div> <div className='software-product-inline-section'> - <ValidationInput + <Input value={description} label={i18n('Description')} - ref='description' - onChange={description => onDataChanged({description})} - validations={{freeEnglishText: true, maxLength: 1000, required: true}} + isRequired={true} + overlayPos='bottom' + onChange={description => onDataChanged({description},SP_CREATION_FORM_NAME)} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} type='textarea' - className='field-section'/> + className='field-section' + data-test-id='new-vsp-description' /> </div> </div> - </ValidationForm> + </Form>} </div> ); } @@ -95,29 +131,47 @@ class SoftwareProductCreationView extends React.Component { getVendorList() { let {finalizedLicenseModelList} = this.props; - return [{enum: '', title: i18n('please select...')}].concat(finalizedLicenseModelList.map(vendor => { - return { - enum: vendor.id, - title: vendor.vendorName - }; - })); + return [{enum: '', title: i18n('please select...')}].concat( + sortByStringProperty(finalizedLicenseModelList, 'vendorName').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}); + onSelectVendor(e) { + const selectedIndex = e.target.selectedIndex; + const vendorId = e.target.options[selectedIndex].value; + this.props.onDataChanged({vendorId},SP_CREATION_FORM_NAME); } - create(){ - this.refs.validationForm.handleFormSubmit(new Event('dummy')); + onSelectSubCategory(e) { + const selectedIndex = e.target.selectedIndex; + const subCategory = e.target.options[selectedIndex].value; + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory},SP_CREATION_FORM_NAME); } submit() { - const {data:softwareProduct, finalizedLicenseModelList} = this.props; + let {data:softwareProduct, finalizedLicenseModelList} = this.props; softwareProduct.vendorName = finalizedLicenseModelList.find(vendor => vendor.id === softwareProduct.vendorId).vendorName; this.props.onSubmit(softwareProduct); } + + validateName(value) { + const {data: {id}, VSPNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: VSPNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Software product by the name \'' + value + '\' already exists. Software product name must be unique')}; + } + + validate() { + this.props.onValidateForm(SP_CREATION_FORM_NAME); + } } export default SoftwareProductCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependencies.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependencies.js new file mode 100644 index 0000000000..9540d3f869 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependencies.js @@ -0,0 +1,40 @@ +/*! + * 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. + */ +import {connect} from 'react-redux'; + +import SoftwareProductDependenciesView from './SoftwareProductDependenciesView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductDependenciesActionHelper from './SoftwareProductDependenciesActionHelper.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductDependencies, softwareProductComponents: {componentsList}} = softwareProduct; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + return { + isReadOnlyMode, + softwareProductDependencies: softwareProductDependencies.length ? softwareProductDependencies : [{sourceId: '', targetId: '', relationType: 'dependsOn', id: 'fake'}], + componentsOptions: componentsList.map(component => ({value: component.id, label: component.name})) + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, version}) => { + return { + onDataChanged: dependenciesList => SoftwareProductDependenciesActionHelper.updateDependencyList(dispatch, {dependenciesList}), + onAddDependency: () => SoftwareProductDependenciesActionHelper.addDependency(dispatch), + onSubmit: (dependenciesList) => SoftwareProductDependenciesActionHelper.saveDependencies(dispatch, {softwareProductId, version, dependenciesList}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductDependenciesView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js new file mode 100644 index 0000000000..e47b33a577 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js @@ -0,0 +1,58 @@ +/*! + * 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. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes} from './SoftwareProductDependenciesConstants.js'; +import uuid from 'uuid-js'; + +function baseUrl(softwareProductId, version) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/component-dependency-model`; +} + +function fetchDependency(softwareProductId, version) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); +} + +function postDependency(softwareProductId, version, dependenciesList) { + let modifedDependencyList = dependenciesList ? dependenciesList.filter(item => item.sourceId && item.targetId) + .map(item => ({sourceId: item.sourceId, targetId: item.targetId, relationType: item.relationType})) : []; + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, {componentDependencyModels:modifedDependencyList}); +} + +const SoftwareProductDependenciesActionHelper = { + updateDependencyList(dispatch, {dependenciesList}) { + dispatch({type: actionTypes.SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE, dependenciesList}); + }, + addDependency(dispatch) { + dispatch({type: actionTypes.ADD_SOFTWARE_PRODUCT_DEPENDENCY}); + }, + fetchDependencies(dispatch, {softwareProductId, version}) { + return fetchDependency(softwareProductId, version).then( response => { + const dependenciesList = response.results ? response.results.map(item => {return {...item, id: uuid.create().toString()};}) : []; + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE, + dependenciesList + }); + }); + }, + saveDependencies(dispatch, {softwareProductId, version, dependenciesList}) { + return postDependency(softwareProductId, version, dependenciesList); + } +}; + +export default SoftwareProductDependenciesActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesConstants.js new file mode 100644 index 0000000000..1f27ed8311 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesConstants.js @@ -0,0 +1,29 @@ +/*! + * 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. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE: null, + ADD_SOFTWARE_PRODUCT_DEPENDENCY: null +}); + +export const relationTypes = { + DEPENDS_ON: 'dependsOn' +}; + +export const relationTypesOptions = [ + {value: relationTypes.DEPENDS_ON, label: 'Depends On'} +]; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesReducer.js new file mode 100644 index 0000000000..3fb479eedc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesReducer.js @@ -0,0 +1,36 @@ + +/*! + * 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. + */ + +import {actionTypes, relationTypes} from './SoftwareProductDependenciesConstants.js'; +import {checkCyclesAndMarkDependencies} from './SoftwareProductDependenciesUtils.js'; +import uuid from 'uuid-js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE: + return checkCyclesAndMarkDependencies(action.dependenciesList); + case actionTypes.ADD_SOFTWARE_PRODUCT_DEPENDENCY: + return [...state, { + sourceId: null, + relationType: relationTypes.DEPENDS_ON, + targetId: null, + id: uuid.create() + }]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesUtils.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesUtils.js new file mode 100644 index 0000000000..94d21bd49d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesUtils.js @@ -0,0 +1,64 @@ +/*! + * 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. + */ + +import DirectedGraph from 'nfvo-utils/DirectedGraph.js'; + +function findCycles(graph, node, id, visited = {}, visitedConnections = {}, recursionStack = {}, connectionsWithCycle = {}) { + visited[node] = true; + recursionStack[node] = true; + if (id) { + visitedConnections[id] = true; + } + for (let edge of graph.getEdges(node)) { + if (!visited[edge.target]) { + findCycles(graph, edge.target, edge.id, visited, visitedConnections, recursionStack, connectionsWithCycle); + } else if (recursionStack[edge.target]) { + visitedConnections[edge.id] = true; + for (let connection in visitedConnections) { + connectionsWithCycle[connection] = true; + } + } + } + recursionStack[node] = false; + return {visitedNodes: visited, connectionsWithCycle: connectionsWithCycle}; +} + +export function checkCyclesAndMarkDependencies(dependenciesList) { + let overallVisitedNodes = {}; + let overallConnectionsWithCycles = {}; + + let g = new DirectedGraph(); + for (let dependency of dependenciesList) { + if (dependency.sourceId !== null && dependency.targetId !== null) { + g.addEdge(dependency.sourceId, dependency.targetId, {id: dependency.id}); + } + } + + for (let node in g.nodes) { + if (!overallVisitedNodes.node) { + let {visitedNodes, connectionsWithCycle} = findCycles(g, node, undefined); + overallVisitedNodes = {...overallVisitedNodes, ...visitedNodes}; + overallConnectionsWithCycles = {...overallConnectionsWithCycles, ...connectionsWithCycle}; + } + } + return dependenciesList.map(dependency => ( + { + ...dependency, + hasCycle: dependency.sourceId && dependency.targetId ? + overallConnectionsWithCycles.hasOwnProperty(dependency.id) + : undefined + })); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx new file mode 100644 index 0000000000..da975a7be2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx @@ -0,0 +1,98 @@ +/*! + * 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. + */ + +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import SelectActionTable from 'nfvo-components/table/SelectActionTable.jsx'; +import SelectActionTableRow from 'nfvo-components/table/SelectActionTableRow.jsx'; +import SelectActionTableCell from 'nfvo-components/table/SelectActionTableCell.jsx'; +import {relationTypesOptions} from './SoftwareProductDependenciesConstants.js'; + +export default class SoftwareProductDependenciesView extends React.Component { + filterTargets({componentsOptions, sourceToTargetMapping, selectedSourceId, selectedTargetId}) { + let isInMap = sourceToTargetMapping.hasOwnProperty(selectedSourceId); + return componentsOptions.filter(component => { + if (component.value === selectedTargetId) { + return true; + } else { + return component.value !== selectedSourceId && (isInMap ? sourceToTargetMapping[selectedSourceId].indexOf(component.value) < 0 : true); + } + }); + } + + filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId, selectedTargetId}) { + return componentsOptions.filter(component => { + if (component.value === selectedSourceId) { + return true; + } else { + let isInMap = sourceToTargetMapping.hasOwnProperty(component.value); + return component.value !== selectedTargetId && (isInMap ? sourceToTargetMapping[component.value].indexOf(selectedTargetId) < 0 : true); + } + }); + } + + render() { + let {componentsOptions, softwareProductDependencies, onDataChanged, onAddDependency, isReadOnlyMode} = this.props; + let canAdd = softwareProductDependencies.length < componentsOptions.length * (componentsOptions.length - 1); + let sourceToTargetMapping = {}; + softwareProductDependencies.map(dependency => { + let isInMap = sourceToTargetMapping.hasOwnProperty(dependency.sourceId); + if (dependency.targetId) { + sourceToTargetMapping[dependency.sourceId] = isInMap ? [...sourceToTargetMapping[dependency.sourceId], dependency.targetId] : [dependency.targetId]; + } + }); + return ( + <div className='software-product-dependencies'> + <div className='software-product-dependencies-title'>{i18n('Dependencies')}</div> + <SelectActionTable + columns={['Source', 'Relation Type', 'Target']} + isReadOnlyMode={isReadOnlyMode} + onAdd={canAdd ? onAddDependency : undefined} + onAddItem={i18n('Add Rule')}> + {softwareProductDependencies.map(dependency => ( + <SelectActionTableRow + key={dependency.id} + onDelete={() => onDataChanged(softwareProductDependencies.filter(currentDependency => currentDependency.id !== dependency.id))} + overlayMsg={i18n('There is a loop between selections')} + hasError={dependency.hasCycle}> + <SelectActionTableCell + options={this.filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} + selected={dependency.sourceId} + placeholder={i18n('Select VFC...')} + onChange={newSourceId => onDataChanged(softwareProductDependencies.map(currentDependency => + ({...currentDependency, sourceId: currentDependency.id === dependency.id ? newSourceId : currentDependency.sourceId}) + ))} /> + <SelectActionTableCell options={relationTypesOptions} selected={dependency.relationType} clearable={false}/> + <SelectActionTableCell + placeholder={i18n('Select VFC...')} + options={this.filterTargets({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} + selected={dependency.targetId} + onChange={newTargetId => onDataChanged(softwareProductDependencies.map(currentDependency => + ({...currentDependency, targetId: currentDependency.id === dependency.id ? newTargetId : currentDependency.targetId}) + ))} /> + </SelectActionTableRow> + ))} + </SelectActionTable> + </div> + ); + } + + save() { + let {onSubmit, softwareProductDependencies} = this.props; + return onSubmit(softwareProductDependencies); + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js index 16a100c664..ac0282e593 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js @@ -1,35 +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 - * + * + * 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========================================================= + * 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. */ - 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'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, licenseModel: {licenseAgreement, featureGroup}}) => { - let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct; + let {softwareProductEditor: {data: currentSoftwareProduct, genericFieldInfo}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct; let {licensingData = {}, licensingVersion} = currentSoftwareProduct; let licenseAgreementList = [], filteredFeatureGroupsList = []; - if(licensingVersion && licensingVersion !== '') { - licenseAgreementList = licenseAgreement.licenseAgreementList; + licenseAgreementList = licenseAgreement.licenseAgreementList; + if(licensingVersion && licensingVersion !== '' && licensingData && licensingData.licenseAgreement) { let selectedLicenseAgreement = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); if (selectedLicenseAgreement) { let featureGroupsList = featureGroup.featureGroupsList.filter(({referencingLicenseAgreements}) => referencingLicenseAgreements.includes(selectedLicenseAgreement.id)); @@ -38,9 +36,11 @@ export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, lic } } } - let {qdata, qschema} = softwareProductQuestionnaire; + let {qdata, qgenericFieldInfo : qGenericFieldInfo, dataMap} = softwareProductQuestionnaire; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + return { currentSoftwareProduct, softwareProductCategories, @@ -48,16 +48,19 @@ export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, lic featureGroupsList: filteredFeatureGroupsList, finalizedLicenseModelList, qdata, - qschema, - isReadOnlyMode + isReadOnlyMode, + isFormValid, + genericFieldInfo, + qGenericFieldInfo, + dataMap }; }; export const mapActionsToProps = (dispatch) => { return { - onDataChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}), - onVendorParamChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData}), - onQDataChanged: ({data}) => SoftwareProductActionHelper.softwareProductQuestionnaireUpdate(dispatch, {data}), + onDataChanged: (deltaData, formName) => ValidationHelper.dataChanged(dispatch, {deltaData, formName}), + onVendorParamChanged: (deltaData, formName) => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData, formName}), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: PRODUCT_QUESTIONNAIRE}), onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}), onSubmit: (softwareProduct, qdata) =>{ return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});} }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js index e060706b37..d62207ff9f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js @@ -1,55 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes, forms} 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, + formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'validateName', data: true}, {type: 'maxLength', data: 25}, {type: 'required', data: true}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + }, data: action.response }; case actionTypes.TOGGLE_NAVIGATION_ITEM: diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx index 75a5797dec..1d52da38b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx @@ -1,9 +1,266 @@ +/*! + * 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. + */ 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 sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import InputOptions from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +class GeneralSection extends React.Component { + static propTypes = { + vendorId: PropTypes.string, + name: PropTypes.string, + description: PropTypes.string, + subCategory: PropTypes.string, + softwareProductCategories: PropTypes.array, + finalizedLicenseModelList: PropTypes.array, + onDataChanged: PropTypes.func.isRequired, + onVendorParamChanged: PropTypes.func.isRequired, + onSelectSubCategory: PropTypes.func.isRequired + }; + + onVendorParamChanged(e) { + const selectedIndex = e.target.selectedIndex; + const vendorId = e.target.options[selectedIndex].value; + this.props.onVendorParamChanged({vendorId}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + + } + + onSelectSubCategory(e) { + const selectedIndex = e.target.selectedIndex; + const subCategory = e.target.options[selectedIndex].value; + this.props.onSelectSubCategory(subCategory); + } + + render (){ + let {genericFieldInfo} = this.props; + return ( + <div> + {genericFieldInfo && <GridSection title={i18n('General')}> + <GridItem> + <Input + data-test-id='vsp-name' + label={i18n('Name')} + type='text' + value={this.props.name} + isRequired={true} + errorText={genericFieldInfo.name.errorText} + isValid={genericFieldInfo.name.isValid} + onChange={name => this.props.onDataChanged({name}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS)}/> + <Input + data-test-id='vsp-vendor-name' + label={i18n('Vendor')} + type='select' + value={this.props.vendorId} + onChange={e => this.onVendorParamChanged(e)}> + {sortByStringProperty( + this.props.finalizedLicenseModelList, + 'vendorName' + ).map(lm => <option key={lm.id} value={lm.id}>{lm.vendorName}</option>) + } + </Input> + <Input + data-test-id='vsp-category-name' + label={i18n('Category')} + type='select' + value={this.props.subCategory} + onChange={e => this.onSelectSubCategory(e)}> + { + this.props.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> + ) + } + </Input> + </GridItem> + <GridItem colSpan={2} stretch> + <Input + data-test-id='vsp-description' + label={i18n('Description')} + type='textarea' + isRequired={true} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + value={this.props.description} + onChange={description => this.props.onDataChanged({description}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS)}/> + </GridItem> + </GridSection>} + </div>); + } +} +class LicensesSection extends React.Component { + static propTypes = { + onVendorParamChanged: PropTypes.func.isRequired, + vendorId: PropTypes.string, + licensingVersion: PropTypes.object, + licensingVersionsList: PropTypes.array, + licensingData: PropTypes.shape({ + licenceAgreement: PropTypes.string, + featureGroups: PropTypes.array + }), + onFeatureGroupsChanged: PropTypes.func.isRequired, + onLicensingDataChanged: PropTypes.func.isRequired, + featureGroupsList: PropTypes.array, + licenseAgreementList: PropTypes.array + }; + + onVendorParamChanged(e) { + const selectedIndex = e.target.selectedIndex; + const licensingVersion = e.target.options[selectedIndex].value; + this.props.onVendorParamChanged({vendorId: this.props.vendorId, licensingVersion:{id:licensingVersion, label: licensingVersion}}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + } + + onLicensingDataChanged(e) { + const selectedIndex = e.target.selectedIndex; + const licenseAgreement = e.target.options[selectedIndex].value; + this.props.onLicensingDataChanged({licenseAgreement, featureGroups: []}); + } + + render(){ + return ( + <GridSection title={i18n('Licenses')}> + <GridItem> + <Input + data-test-id='vsp-licensing-version' + onChange={e => this.onVendorParamChanged(e)} + value={this.props.licensingVersion ? this.props.licensingVersion.id : ''} + label={i18n('Licensing Version')} + type='select'> + {this.props.licensingVersionsList.map(version => + <option + key={version.enum} + value={version.enum}>{version.title} + </option> + )} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-license-agreement' + label={i18n('License Agreement')} + type='select' + value={this.props.licensingData.licenseAgreement ? this.props.licensingData.licenseAgreement : '' } + onChange={(e) => this.onLicensingDataChanged(e)}> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {this.props.licenseAgreementList.map(la => <option value={la.id} key={la.id}>{la.name}</option>)} + </Input> + </GridItem> + <GridItem> + {this.props.licensingData.licenseAgreement && ( + <InputOptions + data-test-id='vsp-feature-group' + type='select' + isMultiSelect={true} + onInputChange={()=>{}} + onEnumChange={featureGroups => this.props.onFeatureGroupsChanged({featureGroups})} + multiSelectedEnum={this.props.licensingData.featureGroups} + name='feature-groups' + label={i18n('Feature Groups')} + clearable={false} + values={this.props.featureGroupsList}/>) + } + </GridItem> + </GridSection> + ); + } +} +const AvailabilitySection = (props) => ( + <GridSection title={i18n('Availability')}> + <GridItem colSpan={2}> + <Input + data-test-id='vsp-use-availability-zone' + label={i18n('Use Availability Zones for High Availability')} + type='checkbox' + value={props.dataMap['general/availability/useAvailabilityZonesForHighAvailability']} + onChange={(aZone) => props.onQDataChanged({'general/availability/useAvailabilityZonesForHighAvailability' : aZone})} /> + </GridItem> + </GridSection> +); +const RegionsSection = (props) => ( + <GridSection title={i18n('Regions')}> + <GridItem> + <InputOptions + data-test-id='vsp-regions' + type='select' + isMultiSelect={true} + onInputChange={()=>{}} + onEnumChange={(regions) => props.onQDataChanged({'general/regionsData/regions' : regions})} + multiSelectedEnum={props.dataMap['general/regionsData/regions']} + name='vsp-regions' + clearable={false} + values={props.genericFieldInfo['general/regionsData/regions'].enum} /> + </GridItem> + </GridSection> +); +const StorageDataReplicationSection = (props) => ( + <GridSection title={i18n('Storage Data Replication')}> + <GridItem> + <Input + data-test-id='vsp-storage-rep-size' + label={i18n('Storage Replication Size (GB)')} + type='number' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationSize'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationSize'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationSize']} + onChange={(sRep) => props.onQDataChanged({'general/storageDataReplication/storageReplicationSize' : sRep})} /> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-storage-rep-source' + label={i18n('Storage Replication Source')} + type='text' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationSource'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationSource'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationSource']} + onChange={(sRepSource) => props.onQDataChanged({'general/storageDataReplication/storageReplicationSource' : sRepSource})} /> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-storage-rep-freq' + label={i18n('Storage Replication Freq. (min)')} + type='number' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationFrequency'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationFrequency'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationFrequency']} + onChange={(sRepFreq) => props.onQDataChanged({'general/storageDataReplication/storageReplicationFrequency' : sRepFreq})} /> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-storage-rep-dest' + label={i18n('Storage Replication Destination')} + type='text' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationDestination'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationDestination'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationDestination']} + onChange={(sRepDest) => props.onQDataChanged({'general/storageDataReplication/storageReplicationDestination' : sRepDest})} /> + </GridItem> + </GridSection> +); class SoftwareProductDetails extends Component { @@ -17,7 +274,7 @@ class SoftwareProductDetails extends Component { subCategory: PropTypes.string, vendorId: PropTypes.string, vendorName: PropTypes.string, - licensingVersion: PropTypes.string, + licensingVersion: PropTypes.object, licensingData: PropTypes.shape({ licenceAgreement: PropTypes.string, featureGroups: PropTypes.array @@ -31,7 +288,6 @@ class SoftwareProductDetails extends Component { onDataChanged: PropTypes.func.isRequired, onValidityChanged: PropTypes.func.isRequired, qdata: PropTypes.object.isRequired, - qschema: PropTypes.object.isRequired, onQDataChanged: PropTypes.func.isRequired, onVendorParamChanged: PropTypes.func.isRequired }; @@ -40,161 +296,63 @@ class SoftwareProductDetails extends Component { licensingVersionsList: [] }; - render() { - let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, featureGroupsList, licenseAgreementList, currentSoftwareProduct} = this.props; - let {name, description, vendorId, licensingVersion, subCategory, licensingData = {}} = currentSoftwareProduct; + prepareDataForGeneralSection(){ + let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, currentSoftwareProduct, genericFieldInfo} = this.props; + let {name, description, vendorId, subCategory} = currentSoftwareProduct; + return { + name, + description, + vendorId, + subCategory, + softwareProductCategories, + finalizedLicenseModelList, + onDataChanged, + onVendorParamChanged: args => this.onVendorParamChanged(args), + onSelectSubCategory: args => this.onSelectSubCategory(args), + genericFieldInfo + }; + + } + + prepareDataForLicensesSection(){ + let { featureGroupsList, licenseAgreementList, currentSoftwareProduct } = this.props; + let {vendorId, licensingVersion, licensingData = {}} = currentSoftwareProduct; let licensingVersionsList = this.state.licensingVersionsList.length > 0 ? this.state.licensingVersionsList : this.refreshVendorVersionsList(vendorId); - let {qdata, qschema, onQDataChanged} = this.props; + return { + onVendorParamChanged: args => this.onVendorParamChanged(args), + vendorId, + licensingVersion, + licensingVersionsList, + licensingData, + onFeatureGroupsChanged: args => this.onFeatureGroupsChanged(args), + onLicensingDataChanged: args => this.onLicensingDataChanged(args), + featureGroupsList, + licenseAgreementList, + }; + + } + + render() { + let {currentSoftwareProduct} = this.props; + let {qdata, onQDataChanged, dataMap, qGenericFieldInfo} = this.props; let {isReadOnlyMode} = this.props; return ( - <div className='vsp-details-page'> + <div className='vsp-details-page'> <Form - ref='validationForm' + ref={(validationForm) => this.validationForm = validationForm} className='vsp-general-tab' hasButtons={false} + formReady={null} + isValid={this.props.isFormValid} 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> + <GeneralSection {...this.prepareDataForGeneralSection()}/> + <LicensesSection {...this.prepareDataForLicensesSection()}/> + <AvailabilitySection onQDataChanged={onQDataChanged} dataMap={dataMap} /> + <RegionsSection onQDataChanged={onQDataChanged} dataMap={dataMap} genericFieldInfo={qGenericFieldInfo} /> + <StorageDataReplicationSection onQDataChanged={onQDataChanged} dataMap={dataMap} genericFieldInfo={qGenericFieldInfo} /> </Form> </div> ); @@ -213,7 +371,9 @@ class SoftwareProductDetails extends Component { licensingVersion, licensingData: {} }; - onVendorParamChanged(deltaData); + + onVendorParamChanged(deltaData, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + } refreshVendorVersionsList(vendorId) { @@ -229,20 +389,14 @@ class SoftwareProductDetails extends Component { }]; if(finalVersions) { finalVersions.forEach(version => licensingVersionsList.push({ - enum: version, - title: version + enum: version.id, + title: version.label })); } return licensingVersionsList; } - onSelectSubCategory(subCategory) { - let {softwareProductCategories, onDataChanged} = this.props; - let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); - onDataChanged({category, subCategory}); - } - onFeatureGroupsChanged({featureGroups}) { this.onLicensingDataChanged({featureGroups}); } @@ -253,11 +407,17 @@ class SoftwareProductDetails extends Component { ...this.props.currentSoftwareProduct.licensingData, ...deltaData } - }); + }, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + } + + onSelectSubCategory(subCategory) { + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); } save(){ - return this.refs.validationForm.handleFormSubmit(new Event('dummy')); + return this.validationForm.handleFormSubmit(new Event('dummy')); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js index 7604f5841d..e8091bf8d1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js @@ -1,33 +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 - * + * + * 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========================================================= + * 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. */ - 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'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; -const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { +export const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct; let {licensingData = {}} = currentSoftwareProduct; let {licenseAgreementList} = licenseAgreement; @@ -35,6 +29,8 @@ const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => let licenseAgreementName = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); if (licenseAgreementName) { licenseAgreementName = licenseAgreementName.name; + } else if (licenseAgreementList.length === 0) { // otherwise the state of traingle svgicon will be updated post unmounting + licenseAgreementName = null; } let categoryName = '', subCategoryName = '', fullCategoryDisplayName = ''; @@ -62,27 +58,44 @@ const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => const mapActionsToProps = (dispatch, {version}) => { return { - onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, { + onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId, version}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, { softwareProductId, - licenseModelId + licenseModelId, + version }), - onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}), + onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}), onUpload: (softwareProductId, formData) => SoftwareProductActionHelper.uploadFile(dispatch, { softwareProductId, formData, - failedNotificationTitle: i18n('Upload validation failed') + failedNotificationTitle: i18n('Upload validation failed'), + version }), + onUploadConfirmation: (softwareProductId, formData) => - SoftwareProductActionHelper.uploadConfirmation(dispatch, { - softwareProductId, - formData, - failedNotificationTitle: i18n('Upload validation failed')}), + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Upload will erase existing data. Do you want to continue?'), + confirmationButtonText: i18n('Continue'), + title: i18n('Warning'), + onConfirmed: ()=>SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed'), + version + }), + onDeclined: () => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }) + } + }), onInvalidFileSizeUpload: () => dispatch({ - type: NotificationConstants.NOTIFY_ERROR, + type: modalActionTypes.GLOBAL_MODAL_ERROR, data: { title: i18n('Upload Failed'), + confirmationButtonText: i18n('Continue'), msg: i18n('no zip file was uploaded or zip file doesn\'t exist') } }), diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx deleted file mode 100644 index 4a848834b2..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx +++ /dev/null @@ -1,38 +0,0 @@ -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 index cf7c7a31a5..5fbf1b74b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import classnames from 'classnames'; import Dropzone from 'react-dropzone'; @@ -6,15 +21,14 @@ 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 ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; -import FontAwesome from 'react-fontawesome'; -import SoftwareProductLandingPageUploadConfirmationModal from './SoftwareProductLandingPageUploadConfirmationModal.jsx'; - +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; const SoftwareProductPropType = React.PropTypes.shape({ name: React.PropTypes.string, description: React.PropTypes.string, - version: React.PropTypes.string, + version: React.PropTypes.object, id: React.PropTypes.string, categoryId: React.PropTypes.string, vendorId: React.PropTypes.string, @@ -79,7 +93,6 @@ class SoftwareProductLandingPageView extends React.Component { { componentsList.length > 0 && this.renderComponents() } - <SoftwareProductLandingPageUploadConfirmationModal confirmationButtonText={i18n('Continue')}/> </div> ); } @@ -101,29 +114,31 @@ class SoftwareProductLandingPageView extends React.Component { onClick={() => onDetailsSelect(currentSoftwareProduct)}> <div className='details-container'> <div className='single-detail-section title-section'> - <div> - <div>{name}</div> + <div className='single-detail-section title-text'> + {name} </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 className='details-section'> + <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> - <div className='single-detail-section'> - <div className='title'>{i18n('Description')}</div> - <div className='description'>{description}</div> + <div className='single-detail-section'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> </div> </div> </div> @@ -151,19 +166,13 @@ class SoftwareProductLandingPageView extends React.Component { <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()}> + <div data-test-id='upload-btn' className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}> <span className='primary-btn-text'>{i18n('Select file')}</span> </div> </div> @@ -180,7 +189,8 @@ class SoftwareProductLandingPageView extends React.Component { title={i18n('Virtual Function Components')} filterValue={localFilter} placeholder={i18n('Filter Components')} - onFilter={filter => this.setState({localFilter: filter})}> + onFilter={value => this.setState({localFilter: value})} + twoColumns> {this.filterList().map(component => this.renderComponentsListItem(component))} </ListEditorView> ); @@ -194,21 +204,19 @@ class SoftwareProductLandingPageView extends React.Component { 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> + <ListEditorItemViewField> <div className='name'>{displayName}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Description')}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> <div className='description'>{description}</div> - </div> + </ListEditorItemViewField> </ListEditorItemView> ); } renderLicenseAgreement(licenseAgreementName) { - if (!licenseAgreementName) { - return (<FontAwesome name='exclamation-triangle' className='warning-icon'/>); + if (licenseAgreementName !== null && !licenseAgreementName) { + return (<div className='missing-license'><SVGIcon name='exclamation-triangle-full'/><div className='warning-text'>{i18n('Missing')}</div></div>); } return (licenseAgreementName); } @@ -242,6 +250,9 @@ class SoftwareProductLandingPageView extends React.Component { this.startUploading(files); } else { + this.setState({ + dragging: false + }); this.props.onInvalidFileSizeUpload(); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js index dadc7777e1..6161eadba9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; import SoftwareProductNetworksView from './SoftwareProductNetworksView.jsx'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js index d0e29bcfe5..4cb460ecc6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js @@ -1,36 +1,31 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductNetworksConstants.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -function baseUrl(svpId) { +function baseUrl(vspId, version) { + let {id: versionId} = version; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${svpId}/networks`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/networks`; } function fetchNetworksList(softwareProductId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); } const 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 index d428d21a26..a3d5578dc4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js index 0c9c62372a..e7c2fcb045 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductNetworksConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx index bd47467fe1..024c5cc44c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx @@ -1,8 +1,24 @@ +/*! + * 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. + */ 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'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; class SoftwareProductNetworksView extends React.Component { @@ -27,7 +43,8 @@ class SoftwareProductNetworksView extends React.Component { title={i18n('Networks')} filterValue={localFilter} placeholder={i18n('Filter Networks')} - onFilter={filter => this.setState({localFilter: filter})}> + onFilter={value => this.setState({localFilter: value})} + twoColumns> {this.filterList().map(network => this.renderNetworksListItem(network))} </ListEditorView> </div> @@ -42,14 +59,15 @@ class SoftwareProductNetworksView extends React.Component { className='list-editor-item-view' isReadOnlyMode={true}> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Name')}</div> + <ListEditorItemViewField> <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> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div className='details'> + <div className='title'>{i18n('DHCP')}</div> + <div className='artifact-name'>{dhcp ? i18n('YES') : i18n('NO')}</div> + </div> + </ListEditorItemViewField> </ListEditorItemView> ); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js index 5c3a8dae01..66926ce4b3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js @@ -1,30 +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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; import SoftwareProductProcessesView from './SoftwareProductProcessesView.jsx'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); let {data} = processesEditor; @@ -42,7 +39,14 @@ const mapActionsToProps = (dispatch, {softwareProductId}) => { return { onAddProcess: () => SoftwareProductProcessesActionHelper.openEditor(dispatch), onEditProcess: (process) => SoftwareProductProcessesActionHelper.openEditor(dispatch, process), - onDeleteProcess: (process) => SoftwareProductProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + onDeleteProcess: (process, version) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + onConfirmed: ()=> SoftwareProductProcessesActionHelper.deleteProcess(dispatch, + {process, softwareProductId, version}) + } + }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js index df5d08ffe5..8fd370b6cc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js @@ -1,62 +1,57 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductProcessesConstants.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -function baseUrl(svpId) { +function baseUrl(vspId, version) { + let {id: versionId} = version; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${svpId}/processes`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/processes`; } -function putProcess(softwareProductId, process) { - return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${process.id}`, { +function putProcess(softwareProductId, version, process) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${process.id}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function postProcess(softwareProductId, process) { - return RestAPIUtil.create(`${baseUrl(softwareProductId)}`, { +function postProcess(softwareProductId, version, process) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function deleteProcess(softwareProductId, processId) { - return RestAPIUtil.destroy(`${baseUrl(softwareProductId)}/${processId}`); +function deleteProcess(softwareProductId, version, processId) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version)}/${processId}`); } -function uploadFileToProcess(softwareProductId, processId, formData) +function uploadFileToProcess(softwareProductId, version, processId, formData) { - return RestAPIUtil.create(`${baseUrl(softwareProductId)}/${processId}/upload`, formData); + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}/${processId}/upload`, formData); } function fetchProcesses(softwareProductId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); } - - const SoftwareProductActionHelper = { fetchProcessesList(dispatch, {softwareProductId, version}) { @@ -80,8 +75,8 @@ const SoftwareProductActionHelper = { }); }, - deleteProcess(dispatch, {process, softwareProductId}) { - return deleteProcess(softwareProductId, process.id).then(() => { + deleteProcess(dispatch, {process, softwareProductId, version}) { + return deleteProcess(softwareProductId, version, process.id).then(() => { dispatch({ type: actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS, processId: process.id @@ -96,18 +91,11 @@ const SoftwareProductActionHelper = { }); }, - processEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.processEditor.DATA_CHANGED, - deltaData - }); - }, - - saveProcess(dispatch, {softwareProductId, previousProcess, process}) { + saveProcess(dispatch, {softwareProductId, version, previousProcess, process}) { if (previousProcess) { - return putProcess(softwareProductId, process).then(() => { + return putProcess(softwareProductId, version, process).then(() => { if (process.formData){ - uploadFileToProcess(softwareProductId, process.id, process.formData); + uploadFileToProcess(softwareProductId, version, process.id, process.formData); } dispatch({ type: actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS, @@ -116,9 +104,9 @@ const SoftwareProductActionHelper = { }); } else { - return postProcess(softwareProductId, process).then(response => { + return postProcess(softwareProductId, version, process).then(response => { if (process.formData) { - uploadFileToProcess(softwareProductId, response.value, process.formData); + uploadFileToProcess(softwareProductId, version, response.value, process.formData); } dispatch({ type: actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx deleted file mode 100644 index 0159352dae..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx +++ /dev/null @@ -1,45 +0,0 @@ -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 index 63f3067a89..6eee24cdde 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -27,8 +22,14 @@ export const actionTypes = keyMirror({ 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 - } + SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM: null }); + +export const optionsInputValues = { + PROCESS_TYPE: [ + {title: 'Select...', enum: ''}, + {title: 'Other', enum: 'Other'} + ] +}; + +export const VSP_PROCESS_FORM = 'VSPPROCESSFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js index 8dc48c50b1..ff787c357e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js @@ -1,31 +1,28 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper'; import SoftwareProductProcessesEditorView from './SoftwareProductProcessesEditorView.jsx'; +import {VSP_PROCESS_FORM} from './SoftwareProductProcessesConstants.js'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; - let {data} = processesEditor; - + let {data, genericFieldInfo, formReady} = processesEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); let previousData; const processId = data ? data.id : null; if(processId) { @@ -34,18 +31,22 @@ const mapStateToProps = ({softwareProduct}) => { return { data, - previousData + genericFieldInfo, + previousData, + isFormValid, + formReady }; }; -const mapActionsToProps = (dispatch, {softwareProductId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version}) => { return { - onDataChanged: deltaData => SoftwareProductProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: VSP_PROCESS_FORM}), onSubmit: ({previousProcess, process}) => { SoftwareProductProcessesActionHelper.closeEditor(dispatch); - SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, process}); + SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, version, previousProcess, process}); }, - onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch) + onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch), + onValidateForm: () => ValidationHelper.validateForm(dispatch, VSP_PROCESS_FORM) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js index cae25e2c89..11b89b17c2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js @@ -1,43 +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 - * + * + * 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========================================================= + * 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. */ - -import {actionTypes} from './SoftwareProductProcessesConstants.js'; +import {actionTypes, VSP_PROCESS_FORM} from './SoftwareProductProcessesConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: return { ...state, + formReady: null, + formName: VSP_PROCESS_FORM, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'artifactName' : { + isValid: true, + errorText: '', + validations: [] + }, + 'type' : { + isValid: true, + errorText: '', + validations: [] + } + }, 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 index c2c4aff382..137e4a2b4e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx @@ -1,18 +1,50 @@ +/*! + * 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. + */ 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'; +import {optionsInputValues as ProcessesOptionsInputValues} from './SoftwareProductProcessesConstants.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, - artifactName: React.PropTypes.string + artifactName: React.PropTypes.string, + type: React.PropTypes.string }); +const FileUploadBox = ({onClick}) => { + return ( + <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={onClick}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + ); +}; + + class SoftwareProductProcessesEditorView extends React.Component { state = { @@ -30,63 +62,89 @@ class SoftwareProductProcessesEditorView extends React.Component { }; render() { - let {data = {}, isReadOnlyMode, onDataChanged, onClose} = this.props; - let {name, description, artifactName} = data; + let {data = {}, isReadOnlyMode, onDataChanged, onClose, genericFieldInfo} = this.props; + let {name, description, artifactName, type} = 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> + <div> + {genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + onSubmit={ () => this.submit() } + onReset={ () => onClose() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm() } + 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={(acceptedFiles, rejectedFiles) => this.handleImportSubmit(acceptedFiles, rejectedFiles)} + onDragEnter={() => this.setState({dragging: true})} + onDragLeave={() => this.setState({dragging: false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput'> + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo.name.isValid} + isRequired={true} + data-test-id='name' + errorText={genericFieldInfo.name.errorText} + label={i18n('Name')} + value={name} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <FileUploadBox onClick={() => this.refs.processEditorFileInput.open()}/> + </GridItem> + </GridSection> + <GridSection> + <GridItem colSpan={2}> + <Input + name='vsp-process-description' + groupClassName='vsp-process-description' + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + label={i18n('Notes')} + value={description} + data-test-id='vsp-process-description' + type='textarea'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Artifacts')} + value={artifactName} + type='text' + disabled/> + <Input + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({type: val});} + } + value={type} + label={i18n('Process Type')} + data-test-id='process-type' + isValid={genericFieldInfo.type.isValid} + errorText={genericFieldInfo.type.errorText} + type='select'> + {ProcessesOptionsInputValues.PROCESS_TYPE.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + </GridSection> + </Dropzone> + </div> + </Form>} + </div> ); } @@ -108,14 +166,24 @@ class SoftwareProductProcessesEditorView extends React.Component { } - handleImportSubmit(files) { - let {onDataChanged} = this.props; - this.setState({ - dragging: false, - complete: '0', - files - }); - onDataChanged({artifactName: files[0].name}); + handleImportSubmit(files, rejectedFiles) { + if (files.length > 0) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } + else if (rejectedFiles.length > 0) { + this.setState({ + dragging: false + }); + if (DEBUG) { + console.log('file was rejected.' + rejectedFiles[0].name); + } + } } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js index 619a2dba0f..20390d1f60 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============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 - * + * + * 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========================================================= + * 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. */ - import {actionTypes} from './SoftwareProductProcessesConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx index a2aa3d414e..8f52434042 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx @@ -1,3 +1,18 @@ +/*! + * 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. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Modal from 'nfvo-components/modal/Modal.jsx'; @@ -6,7 +21,7 @@ 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 { @@ -20,30 +35,29 @@ class SoftwareProductProcessesView extends React.Component { onEditProcess: React.PropTypes.func.isRequired, onDeleteProcess: React.PropTypes.func.isRequired, isDisplayEditor: React.PropTypes.bool.isRequired, - isReadOnlyMode: React.PropTypes.bool.isRequired + isReadOnlyMode: React.PropTypes.bool.isRequired, + currentSoftwareProduct:React.PropTypes.object }; 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; + let {currentSoftwareProduct: {id, version}, isModalInEditMode, isReadOnlyMode, isDisplayEditor} = this.props; return ( - <Modal show={isDisplayEditor} bsSize='large' animation={true}> + <Modal show={isDisplayEditor} bsSize='large' animation={true} className='onborading-modal'> <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}/> + <SoftwareProductProcessesEditor softwareProductId={id} version={version} isReadOnlyMode={isReadOnlyMode}/> </Modal.Body> </Modal> ); @@ -60,7 +74,8 @@ class SoftwareProductProcessesView extends React.Component { placeholder={i18n('Filter Process')} onAdd={onAddProcess} isReadOnlyMode={isReadOnlyMode} - onFilter={filter => this.setState({localFilter: filter})}> + title={i18n('Process Details')} + onFilter={value => this.setState({localFilter: value})}> {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} </ListEditorView> ); @@ -68,14 +83,14 @@ class SoftwareProductProcessesView extends React.Component { renderProcessListItem(process, isReadOnlyMode) { let {id, name, description, artifactName = ''} = process; - let {onEditProcess, onDeleteProcess} = this.props; + let {currentSoftwareProduct: {version}, onEditProcess, onDeleteProcess} = this.props; return ( <ListEditorItemView key={id} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode} onSelect={() => onEditProcess(process)} - onDelete={() => onDeleteProcess(process)}> + onDelete={() => onDeleteProcess(process, version)}> <div className='list-editor-item-view-field'> <div className='title'>{i18n('Name')}</div> |