diff options
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/softwareProduct')
101 files changed, 5250 insertions, 2757 deletions
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> |