diff options
author | Michael Lando <ml636r@att.com> | 2017-02-19 12:57:33 +0200 |
---|---|---|
committer | Michael Lando <ml636r@att.com> | 2017-02-19 13:47:13 +0200 |
commit | efa037d34be7b1570efdc767c79fad8d4005f10e (patch) | |
tree | cf1036ba2728dea8a61492b678fa91954e629403 /openecomp-ui/src/sdc-app/onboarding/softwareProduct | |
parent | f5f13c4f6b6fe3b4d98e349dfd7db59339803436 (diff) |
Add new code new version
Change-Id: Ic02a76313503b526f17c3df29eb387a29fe6a42a
Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/softwareProduct')
72 files changed, 6366 insertions, 0 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js new file mode 100644 index 0000000000..2dbef6baf2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -0,0 +1,321 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; + +import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; + +import {navigationItems} from './SoftwareProductConstants.js'; +import SoftwareProductActionHelper from './SoftwareProductActionHelper.js'; +import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; + +const buildComponentNavigationBarGroups = ({componentId, meta}) => { + const groups = ([ + { + id: navigationItems.GENERAL + '|' + componentId, + name: i18n('General'), + disabled: false, + meta + }, { + id: navigationItems.COMPUTE + '|' + componentId, + name: i18n('Compute'), + disabled: false, + meta + }, { + id: navigationItems.LOAD_BALANCING + '|' + componentId, + name: i18n('High Availability & Load Balancing'), + disabled: false, + meta + }, { + id: navigationItems.NETWORKS + '|' + componentId, + name: i18n('Networks'), + disabled: false, + meta + }, { + id: navigationItems.STORAGE + '|' + componentId, + name: i18n('Storage'), + disabled: false, + meta + }, { + id: navigationItems.PROCESS_DETAILS + '|' + componentId, + name: i18n('Process Details'), + disabled: false, + meta + }, { + id: navigationItems.MONITORING + '|' + componentId, + name: i18n('Monitoring'), + disabled: false, + meta + } + ]); + + return groups; +}; + +const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) => { + const {softwareProductEditor: {data: currentSoftwareProduct = {}}} = softwareProduct; + const {id, name} = currentSoftwareProduct; + const groups = [{ + id: id, + name: name, + items: [ + { + id: navigationItems.VENDOR_SOFTWARE_PRODUCT, + name: i18n('Overview'), + disabled: false, + meta + }, { + id: navigationItems.GENERAL, + name: i18n('General'), + disabled: false, + meta + }, { + id: navigationItems.PROCESS_DETAILS, + name: i18n('Process Details'), + disabled: false, + meta + }, { + id: navigationItems.NETWORKS, + name: i18n('Networks'), + disabled: false, + meta + }, { + id: navigationItems.ATTACHMENTS, + name: i18n('Attachments'), + disabled: false, + meta + }, { + id: navigationItems.COMPONENTS, + name: i18n('Components'), + hidden: componentsList.length <= 0, + meta, + expanded: mapOfExpandedIds[navigationItems.COMPONENTS] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, + items: [ + ...componentsList.map(({id, displayName}) => ({ + id: navigationItems.COMPONENTS + '|' + id, + name: displayName, + meta, + expanded: mapOfExpandedIds[navigationItems.COMPONENTS + '|' + id] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, + items: buildComponentNavigationBarGroups({componentId: id, meta}) + })) + ] + } + ] + }]; + let activeItemId = ({ + [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: navigationItems.VENDOR_SOFTWARE_PRODUCT, + [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: navigationItems.GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: navigationItems.ATTACHMENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: navigationItems.COMPONENTS + })[screen]; + + if(componentId) { + activeItemId = + Object.keys(mapOfExpandedIds).length === 1 && mapOfExpandedIds[navigationItems.COMPONENTS] === true ? + navigationItems.COMPONENTS : ({ + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: navigationItems.GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: navigationItems.COMPUTE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: navigationItems.LOAD_BALANCING, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING + })[screen] + '|' + componentId; + } + + return { + activeItemId, groups + }; +}; + +const buildVersionControllerProps = (softwareProduct) => { + const {softwareProductEditor} = softwareProduct; + const {data: currentSoftwareProduct = {}, isValidityData = true} = softwareProductEditor; + + const {version, viewableVersions, status: currentStatus, lockingUser} = currentSoftwareProduct; + const {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ? + VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) : + {status: currentStatus, isCheckedOut: false}; + + return { + status, isCheckedOut, version, viewableVersions, + isFormDataValid: isValidityData + }; +}; + +const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { + const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire} = softwareProduct; + const {data: currentSoftwareProduct = {}, mapOfExpandedIds = []} = softwareProductEditor; + const {version} = currentSoftwareProduct; + const {componentsList = []} = softwareProductComponents; + const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + const {qdata} = softwareProductQuestionnaire; + let currentComponentMeta = {}; + if(componentId) { + const {componentEditor: {data: componentData = {} , qdata: componentQdata}} = softwareProductComponents; + currentComponentMeta = {componentData, componentQdata}; + } + const meta = {softwareProduct: currentSoftwareProduct, qdata, version, isReadOnlyMode, currentComponentMeta}; + return { + versionControllerProps: buildVersionControllerProps(softwareProduct), + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) + }; +}; + +const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, meta: {isReadOnlyMode, softwareProduct, qdata, currentComponentMeta: {componentData, componentQdata}}}) => { + let promise; + if (isReadOnlyMode) { + promise = Promise.resolve(); + } else { + switch(screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: + promise = SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata}); + break; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, componentData, qdata: componentQdata}); + break; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata: componentQdata}); + break; + default: + promise = Promise.resolve(); + break; + } + } + return promise; +}; + + +const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentComponentId}) => { + const [nextScreen, nextComponentId] = id.split('|'); + switch(nextScreen) { + case navigationItems.COMPONENTS: + if(nextComponentId === currentComponentId) { + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId}); + } else { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version}); + } + break; + case navigationItems.GENERAL: + OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version}); + break; + case navigationItems.COMPUTE: + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.LOAD_BALANCING: + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.NETWORKS: + OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version}); + break; + case navigationItems.STORAGE: + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.PROCESS_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.MONITORING: + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + } +}; + +const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwareProductId, componentId: currentComponentId}}}) => { + + const props = { + onClose: ({version}) => { + if (screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE) { + OnboardingActionHelper.navigateToOnboardingCatalog(dispatch); + } else { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); + } + }, + onVersionSwitching: (version) => { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); + }, + onToggle: (groups, itemIdToExpand) => groups.map(({items}) => SoftwareProductActionHelper.toggleNavigationItems(dispatch, {items, itemIdToExpand})), + onNavigate: ({id, meta}) => { + let preNavigate = autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}); + preNavigate.then(() => { + switch(id) { + case navigationItems.VENDOR_SOFTWARE_PRODUCT: + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: meta.version}); + break; + case navigationItems.GENERAL: + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId}); + break; + case navigationItems.PROCESS_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: meta.version}); + break; + case navigationItems.NETWORKS: + OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: meta.version}); + break; + case navigationItems.ATTACHMENTS: + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + break; + case navigationItems.COMPONENTS: + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId}); + break; + default: + onComponentNavigate(dispatch, {id, softwareProductId, version: meta.version, screen, currentComponentId}); + break; + } + }); + } + }; + + switch (screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + props.onSave = () => { + return Promise.resolve(); + }; + break; + default: + props.onSave = ({softwareProduct, qdata}) => SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata}); + break; + } + + + props.onVersionControllerAction = (action) => + SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action}).then(() => { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); + }); + + return props; +}; + +export default connect(mapStateToProps, mapActionsToProps)(TabulatedEditor); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js new file mode 100644 index 0000000000..d9ed8af679 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -0,0 +1,333 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; + +import {actionTypes} from './SoftwareProductConstants.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; +import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/`; +} +function softwareProductCategoriesUrl() { + const restATTPrefix = Configuration.get('restATTPrefix'); + return `${restATTPrefix}/v1/categories/resources/`; +} + +function uploadFile(vspId, formData) { + + return RestAPIUtil.create(`${baseUrl()}${vspId}/upload`, formData); + +} + +function putSoftwareProduct(softwareData) { + return RestAPIUtil.save(`${baseUrl()}${softwareData.id}`, { + name: softwareData.name, + description: softwareData.description, + category: softwareData.category, + subCategory: softwareData.subCategory, + vendorId: softwareData.vendorId, + vendorName: softwareData.vendorName, + licensingVersion: softwareData.licensingVersion, + icon: softwareData.icon, + licensingData: softwareData.licensingData + }); +} + +function putSoftwareProductQuestionnaire(vspId, qdata) { + return RestAPIUtil.save(`${baseUrl()}${vspId}/questionnaire`, qdata); +} + +function putSoftwareProductAction(id, action) { + return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +} + +function fetchSoftwareProductList() { + return RestAPIUtil.fetch(baseUrl()); +} + +function fetchSoftwareProduct(vspId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl()}${vspId}${versionQuery}`); +} + +function fetchSoftwareProductQuestionnaire(vspId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl()}${vspId}/questionnaire${versionQuery}`); +} + +function objToString(obj) { + let str = ''; + if (obj instanceof Array) { + obj.forEach((item) => { + str += objToString(item) + '\n'; + }); + } else { + for (let p in obj) { + if (obj.hasOwnProperty(p)) { + str += obj[p] + '\n'; + } + } + } + return str; +} + +function parseUploadErrorMsg(error) { + let message = ''; + for (let key in error) { + if (error.hasOwnProperty(key)) { + message += objToString(error[key]) + '\n'; + } + } + return message; +} + +function fetchSoftwareProductCategories(dispatch) { + let handleResponse = response => dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED, + softwareProductCategories: response + }); + return RestAPIUtil.fetch(softwareProductCategoriesUrl()) + .then(handleResponse) + .fail(() => handleResponse(null)); +} + +function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) { + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}); + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion}); +} + +function getExpandedItemsId(items, itemIdToToggle) { + for(let i = 0; i < items.length; i++) { + if(items[i].id === itemIdToToggle) { + if (items[i].expanded) { + return {}; + } else { + return {[itemIdToToggle]: true}; + } + } + else if(items[i].items && items[i].items.length > 0) { + let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle); + if (mapOfExpandedIds !== false) { + mapOfExpandedIds[items[i].id] = true; + return mapOfExpandedIds; + } + } + } + return false; +} + +const SoftwareProductActionHelper = { + + loadSoftwareProductAssociatedData(dispatch) { + fetchSoftwareProductCategories(dispatch); + LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); + }, + + loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) { + SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); + loadLicensingData(dispatch, {licenseModelId, licensingVersion}); + }, + + fetchSoftwareProductList(dispatch) { + return fetchSoftwareProductList().then(response => dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED, + response + })); + }, + + uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle}) { + Promise.resolve() + .then(() => uploadFile(softwareProductId, formData)) + .then(response => { + if (response.status !== 'Success') { + throw new Error(parseUploadErrorMsg(response.errors)); + } + }) + .then(() => { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); + }) + .catch(error => { + dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {title: failedNotificationTitle, msg: error.message} + }); + }); + }, + + uploadConfirmation(dispatch, {softwareProductId, formData, failedNotificationTitle}) { + dispatch({ + type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION, + uploadData: { + softwareProductId, + formData, + failedNotificationTitle + } + }); + }, + hideUploadConfirm (dispatch) { + dispatch({ + type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION + }); + }, + updateSoftwareProduct(dispatch, {softwareProduct, qdata}) { + return Promise.all([ + SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then( + () => dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT, + payload: {softwareProduct} + }) + ), + SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, { + softwareProductId: softwareProduct.id, + qdata + }) + ]); + }, + + updateSoftwareProductData(dispatch, {softwareProduct}) { + return putSoftwareProduct(softwareProduct); + }, + + updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata}) { + return putSoftwareProductQuestionnaire(softwareProductId, qdata); + }, + + softwareProductEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.softwareProductEditor.DATA_CHANGED, + deltaData + }); + }, + + softwareProductQuestionnaireUpdate(dispatch, {data}) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE, + payload: {qdata: data} + }); + }, + + softwareProductEditorVendorChanged(dispatch, {deltaData}) { + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion}); + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion}); + SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}); + }, + + setIsValidityData(dispatch, {isValidityData}) { + dispatch({ + type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED, + isValidityData + }); + }, + + addSoftwareProduct(dispatch, {softwareProduct}) { + dispatch({ + type: actionTypes.ADD_SOFTWARE_PRODUCT, + softwareProduct + }); + }, + + fetchSoftwareProduct(dispatch, {softwareProductId, version}) { + return Promise.all([ + fetchSoftwareProduct(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + return response; + }), + fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE, + payload: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + } + }); + }) + ]); + }, + + performVCAction(dispatch, {softwareProductId, action}) { + if (action === VersionControllerActionsEnum.SUBMIT) { + return putSoftwareProductAction(softwareProductId, action).then(() => { + return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE).then(() => { + dispatch({ + type: NotificationConstants.NOTIFY_SUCCESS, + data: { + title: i18n('Submit Succeeded'), + msg: i18n('This software product successfully submitted'), + timeout: 2000 + } + }); + fetchSoftwareProduct(softwareProductId).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + }); + }); + }, error => dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {title: i18n('Submit Failed'), validationResponse: error.responseJSON} + })); + } + else { + return putSoftwareProductAction(softwareProductId, action).then(() => { + fetchSoftwareProduct(softwareProductId).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + }); + }); + } + }, + + switchVersion(dispatch, {softwareProductId, licenseModelId, version}) { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version}); + }, + + toggleNavigationItems(dispatch, {items, itemIdToExpand}) { + let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand); + dispatch({ + type: actionTypes.TOGGLE_NAVIGATION_ITEM, + mapOfExpandedIds + }); + }, + + /** for the next verision */ + addComponent(dispatch) { + return dispatch; + } +}; + +export default SoftwareProductActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js new file mode 100644 index 0000000000..812afe5409 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +export default { + + getCurrentCategoryOfSubCategory(selectedSubCategory, softwareProductCategories) { + let category, subCategory; + for (var i = 0; i < softwareProductCategories.length; i++) { + let {subcategories = []} = softwareProductCategories[i]; + subCategory = subcategories.find(sub => sub.uniqueId === selectedSubCategory); + if (subCategory) { + category = softwareProductCategories[i].uniqueId; + break; + } + } + return category; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js new file mode 100644 index 0000000000..5f10c27084 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + SOFTWARE_PRODUCT_LOADED: null, + SOFTWARE_PRODUCT_LIST_LOADED: null, + SOFTWARE_PRODUCT_LIST_EDIT: null, + SOFTWARE_PRODUCT_CATEGORIES_LOADED: null, + SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE: null, + ADD_SOFTWARE_PRODUCT: null, + TOGGLE_NAVIGATION_ITEM: null, + + softwareProductEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + IS_VALIDITY_DATA_CHANGED: null, + UPLOAD_CONFIRMATION: null + } +}); + +export const navigationItems = keyMirror({ + VENDOR_SOFTWARE_PRODUCT: 'Vendor Software Product', + GENERAL: 'General', + PROCESS_DETAILS: 'Process Details', + NETWORKS: 'Networks', + ATTACHMENTS: 'Attachments', + COMPONENTS: 'Components', + + COMPUTE: 'Compute', + LOAD_BALANCING: 'Load Balancing', + STORAGE: 'Storage', + MONITORING: 'Monitoring' +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js new file mode 100644 index 0000000000..6d1db1626f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_LIST_LOADED: + return [...action.response.results]; + case actionTypes.SOFTWARE_PRODUCT_LIST_EDIT: + const indexForEdit = state.findIndex(vsp => vsp.id === action.payload.softwareProduct.id); + return [...state.slice(0, indexForEdit), action.payload.softwareProduct, ...state.slice(indexForEdit + 1)]; + case actionTypes.ADD_SOFTWARE_PRODUCT: + return [...state, action.softwareProduct]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js new file mode 100644 index 0000000000..784ac9db84 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {combineReducers} from 'redux'; +import {actionTypes} from './SoftwareProductConstants.js'; +import SoftwareProductAttachmentsReducer from './attachments/SoftwareProductAttachmentsReducer.js'; +import SoftwareProductCreationReducer from './creation/SoftwareProductCreationReducer.js'; +import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js'; +import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js'; +import SoftwareProductProcessesEditorReducer from './processes/SoftwareProductProcessesEditorReducer.js'; +import SoftwareProductNetworksListReducer from './networks/SoftwareProductNetworksListReducer.js'; +import SoftwareProductComponentsListReducer from './components/SoftwareProductComponentsListReducer.js'; +import SoftwareProductComponentEditorReducer from './components/SoftwareProductComponentEditorReducer.js'; +import {actionTypes as processesActionTypes} from './processes/SoftwareProductProcessesConstants.js'; +import SoftwareProductComponentProcessesListReducer from './components/processes/SoftwareProductComponentProcessesListReducer.js'; +import SoftwareProductComponentProcessesEditorReducer from './components/processes/SoftwareProductComponentProcessesEditorReducer.js'; +import {actionTypes as componentProcessesActionTypes} from './components/processes/SoftwareProductComponentProcessesConstants.js'; +import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js'; +import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; +import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; + +export default combineReducers({ + softwareProductAttachments: SoftwareProductAttachmentsReducer, + softwareProductCreation: SoftwareProductCreationReducer, + softwareProductEditor: SoftwareProductDetailsReducer, + softwareProductProcesses: combineReducers({ + processesList: SoftwareProductProcessesListReducer, + processesEditor: SoftwareProductProcessesEditorReducer, + processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state + }), + softwareProductNetworks: combineReducers({ + networksList: SoftwareProductNetworksListReducer + }), + softwareProductComponents: combineReducers({ + componentsList: SoftwareProductComponentsListReducer, + componentEditor: SoftwareProductComponentEditorReducer, + componentProcesses: combineReducers({ + processesList: SoftwareProductComponentProcessesListReducer, + processesEditor: SoftwareProductComponentProcessesEditorReducer, + processToDelete: (state = false, action) => action.type === componentProcessesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM ? action.processToDelete : state, + }), + network: combineReducers({ + nicList: SoftwareProductComponentsNICListReducer, + nicEditor: SoftwareProductComponentsNICEditorReducer + }), + monitoring: SoftwareProductComponentsMonitoringReducer + }), + softwareProductCategories: (state = [], action) => { + if (action.type === actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED) { + return action.softwareProductCategories; + } + return state; + }, + softwareProductQuestionnaire: (state = {}, action) => { + if (action.type === actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE) { + return { + ...state, + ...action.payload + }; + } + return state; + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js new file mode 100644 index 0000000000..a4b95a4b7e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductAttachmentsView from './SoftwareProductAttachmentsView.jsx'; +import SoftwareProductAttachmentsActionHelper from './SoftwareProductAttachmentsActionHelper.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments}}) => { + let {attachmentsTree, hoveredNode, selectedNode, errorList} = softwareProductAttachments; + return { + attachmentsTree, + hoveredNode, + selectedNode, + errorList + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + toggleExpanded: (path) => SoftwareProductAttachmentsActionHelper.toggleExpanded(dispatch, {path}), + onSelectNode: (nodeName) => SoftwareProductAttachmentsActionHelper.onSelectNode(dispatch, {nodeName}), + onUnselectNode: () => SoftwareProductAttachmentsActionHelper.onUnselectNode(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductAttachmentsView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js new file mode 100644 index 0000000000..a7f7a5173b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; + +export default { + + toggleExpanded(dispatch, {path}) { + dispatch({ + type: actionTypes.TOGGLE_EXPANDED, + path + }); + }, + + onSelectNode(dispatch, {nodeName}) { + dispatch({ + type: actionTypes.SELECTED_NODE, + nodeName + }); + }, + + onUnselectNode(dispatch) { + dispatch({ + type: actionTypes.UNSELECTED_NODE + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js new file mode 100644 index 0000000000..33af476d9c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + TOGGLE_EXPANDED: null, + SELECTED_NODE: null, + UNSELECTED_NODE: null +}); + +export const errorTypes = keyMirror({ + MISSING_FILE_IN_ZIP: i18n('missing file in zip'), + MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'), + MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'), + FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'), + FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'), + ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'), + ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'), + MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'), + MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'), + ARTIFACT_NOT_IN_USE: i18n('artifact not in use') +}); + +export const nodeTypes = keyMirror({ + heat: i18n('Heat'), + volume: i18n('Volume'), + network: i18n('Network'), + artifact: i18n('Artifact'), + env: i18n('Environment'), + other: i18n('') +}); + +export const mouseActions = keyMirror({ + MOUSE_BUTTON_CLICK: 0 +}); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js new file mode 100644 index 0000000000..5c5567b032 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js @@ -0,0 +1,199 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; + +const mapVolumeData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'volume', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapNetworkData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'network', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapArtifactsData = ({fileName, errors}) => ({ + name: fileName, + type: 'artifact', + errors +}); + +const mapOtherData = ({fileName, errors}) => ({ + name: fileName, + type: 'other', + errors +}); + + +const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({ + name: fileName, + expanded: true, + type: 'heat', + errors, + children: [ + ...(volume ? volume.map(mapVolumeData) : []), + ...(network ? network.map(mapNetworkData) : []), + ...(env ? [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }] : []), + ...(artifacts ? artifacts.map(mapArtifactsData) : []), + ...(other ? other.map(mapOtherData) : []), + ...(nested ? nested.map(mapHeatData) : []) + ] +}); + +function createErrorList(node, parent, deep = 0, errorList = []) { + if (node.errors) { + errorList.push(...node.errors.map((error) => ({ + errorLevel: error.level, + errorMessage: error.message, + name: node.name, + hasParent: deep > 2, + parentName: parent.name, + type: node.type, + }))); + } + if (node.children && node.children.length) { + node.children.map((child) => createErrorList(child, node, deep + 1, errorList)); + } + return errorList; +} + +const mapValidationDataToTree = validationData => { + let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {}; + return { + children: [ + { + name: 'HEAT', + expanded: true, + type: 'heat', + children: (HEAT ? HEAT.map(mapHeatData) : []) + }, + ...(artifacts ? [{ + name: 'artifacts', + expanded: true, + type: 'artifact', + children: (artifacts ? artifacts.map(mapArtifactsData) : []) + }] : []), + ...(network ? [{ + name: 'networks', + expanded: true, + type: 'network', + children: (network ? network.map(mapNetworkData) : []), + }] : []), + ...(volume ? [{ + name: 'volume', + expanded: true, + type: 'volume', + children: (volume ? volume.map(mapVolumeData) : []), + }] : []), + ...(other ? [{ + name: 'other', + expanded: true, + type: 'other', + children: (other ? other.map(mapOtherData) : []), + }] : []) + ] + }; +}; + +const toggleExpanded = (node, path) => { + let newNode = {...node}; + if (path.length === 0) { + newNode.expanded = !node.expanded; + } else { + let index = path[0]; + newNode.children = [ + ...node.children.slice(0, index), + toggleExpanded(node.children[index], path.slice(1)), + ...node.children.slice(index + 1) + ]; + } + return newNode; +}; + +const expandSelected = (node, selectedNode) => { + let shouldExpand = node.name === selectedNode; + let children = node.children && node.children.map(child => { + let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode); + shouldExpand = shouldExpand || shouldExpandChild; + return newChild; + }); + + return { + node: { + ...node, + expanded: node.expanded || shouldExpand, + children + }, + shouldExpand + }; +}; + +export default (state = {attachmentsTree: {}}, action) => { + switch (action.type) { + case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED: + let currentSoftwareProduct = action.response; + let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {}; + let errorList = createErrorList(attachmentsTree); + return { + ...state, + attachmentsTree, + errorList + }; + case actionTypes.TOGGLE_EXPANDED: + return { + ...state, + attachmentsTree: toggleExpanded(state.attachmentsTree, action.path) + }; + case actionTypes.SELECTED_NODE: + let selectedNode = action.nodeName; + return { + ...state, + attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node, + selectedNode + }; + case actionTypes.UNSELECTED_NODE: + return { + ...state, + selectedNode: undefined + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx new file mode 100644 index 0000000000..c52999ca46 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx @@ -0,0 +1,182 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import classNames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {nodeTypes, mouseActions} from './SoftwareProductAttachmentsConstants'; + +const typeToIcon = Object.freeze({ + heat: 'building-o', + volume: 'database', + network: 'cloud', + artifact: 'gear', + env: 'server', + other: 'cube' +}); + +const leftPanelWidth = 250; + +class SoftwareProductAttachmentsView extends React.Component { + + static propTypes = { + attachmentsTree: React.PropTypes.object.isRequired + }; + state = { + treeWidth: '400' + }; + + render() { + let {attachmentsTree, errorList} = this.props; + let {treeWidth} = this.state; + return ( + <div className='software-product-attachments'> + <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}> + <div className='tree-wrapper'> + { + attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind])) + } + </div> + </div> + <div onMouseDown={(e) => this.onChangeTreeWidth(e)} className='software-product-attachments-separator'/> + + <div className='software-product-attachments-error-list'> + {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ? + i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>} + </div> + </div> + ); + } + + renderNode(node, path) { + let isFolder = node.children && node.children.length > 0; + let {onSelectNode} = this.props; + return ( + <div key={node.name} className='tree-block-inside'> + { + <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}> + { + isFolder && + <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}> + <FontAwesome name='caret-down'/> + </div> + } + { + + <span className='tree-node-icon'> + <FontAwesome name={typeToIcon[node.type]}/> + </span> + } + { + + <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}> + {node.name} + </span> + } + </div> + } + { + isFolder && + <Collapse in={node.expanded}> + <div className='tree-node-children'> + { + node.children.map((child, ind) => this.renderNode(child, [...path, ind])) + } + </div> + </Collapse> + } + </div> + ); + } + + createErrorList(errorList, node, parent) { + if (node.errors) { + node.errors.forEach(error => errorList.push({ + error, + name: node.name, + parentName: parent.name, + type: node.type + })); + } + if (node.children && node.children.length) { + node.children.map((child) => this.createErrorList(errorList, child, node)); + } + } + + renderErrorList(errors) { + let prevError = {}; + let {selectedNode} = this.props; + return errors.map(error => { + let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName; + prevError = error; + + return ( + <div + key={error.name + error.errorMessage + error.parentName} + + onClick={() => this.selectNode(error.name)} + className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}> + <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}> + { + error.hasParent ? + i18n('{type} {name} in {parentName}: ', { + type: nodeTypes[error.type], + name: error.name, + parentName: error.parentName + }) : + i18n('{type} {name}: ', { + type: nodeTypes[error.type], + name: error.name + }) + } + </span> + <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span> + </div> + ); + }); + } + + selectNode(currentSelectedNode) { + let {onUnselectNode, onSelectNode, selectedNode} = this.props; + if (currentSelectedNode !== selectedNode) { + onSelectNode(currentSelectedNode); + }else{ + onUnselectNode(); + } + + } + + getTreeRowClassName(name) { + let {hoveredNode, selectedNode} = this.props; + return classNames({ + 'tree-node-row': true, + 'tree-node-selected': name === hoveredNode, + 'tree-node-clicked': name === selectedNode + }); + } + + getTreeTextClassName(node) { + let {selectedNode} = this.props; + return classNames({ + 'tree-element-text': true, + 'error-status': node.errors, + 'error-status-selected': node.name === selectedNode + }); + } + + onChangeTreeWidth(e) { + if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { + let onMouseMove = (e) => { + this.setState({treeWidth: e.clientX - leftPanelWidth}); + }; + let onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } + } +} + +export default SoftwareProductAttachmentsView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js new file mode 100644 index 0000000000..3b8bc4f171 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.COMPONENT_UPDATE: + return { + ...state, + data: action.component + }; + case actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE: + return { + ...state, + qdata: action.payload.qdata || state.qdata, + qschema: action.payload.qschema || state.qschema + }; + case actionTypes.COMPONENT_DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js new file mode 100644 index 0000000000..e53b2ecafe --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -0,0 +1,129 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import {actionTypes} from './SoftwareProductComponentsConstants.js'; + +function baseUrl(softwareProductId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components`; +} + +function fetchSoftwareProductComponents(softwareProductId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); +} + +function putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, vspComponent) { + return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire`, vspComponent); +} + +function fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version){ + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire${versionQuery}`); +} + +function fetchSoftwareProductComponent(softwareProductId, vspComponentId, version){ + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}${versionQuery}`); +} + +function putSoftwareProductComponent(softwareProductId, vspComponentId, vspComponent) { + return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}`, { + name: vspComponent.name, + displayName: vspComponent.displayName, + description: vspComponent.description + }); +} + +const SoftwareProductComponentsActionHelper = { + fetchSoftwareProductComponents(dispatch, {softwareProductId, version}) { + return fetchSoftwareProductComponents(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); + }); + }, + + componentDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.COMPONENT_DATA_CHANGED, + deltaData + }); + }, + + + updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, componentData, qdata}) { + return Promise.all([ + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}), + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) + ]); + }, + + updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}) { + return putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, qdata); + }, + + updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) { + return putSoftwareProductComponent(softwareProductId, vspComponentId, componentData).then(() => dispatch({ + type: actionTypes.COMPONENTS_LIST_EDIT, + component: { + id: vspComponentId, + ...componentData + } + })); + }, + + + fetchSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, version}) { + return fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version).then(response => { + dispatch({ + type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, + payload: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + } + }); + }); + }, + + fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, version}) { + return fetchSoftwareProductComponent(softwareProductId, vspComponentId, version).then(response => { + dispatch({ + type: actionTypes.COMPONENT_UPDATE, + component: response.data + }); + }); + }, + + componentQuestionnaireUpdated(dispatch, {data}) { + dispatch({ + type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, + payload: { + qdata: data + } + }); + }, +}; + +export default SoftwareProductComponentsActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js new file mode 100644 index 0000000000..dee517e5d1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + COMPONENTS_LIST_UPDATE: null, + COMPONENTS_LIST_EDIT: null, + COMPONENT_UPDATE: null, + COMPONENT_DATA_CHANGED: null, + COMPONENT_QUESTIONNAIRE_UPDATE: null +}); + +export const storageConstants = keyMirror({ + backupType: { + ON_SITE: 'OnSite', + OFF_SITE: 'OffSite' + } +}); + +export const navigationItems = keyMirror({ + STORAGE: 'Storage', + PROCESS_DETAILS: 'Process Details', + MONITORING: 'Monitoring', + NETWORK: 'Network', + COMPUTE: 'Compute', + NETWORK: 'Network', + LOAD_BALANCING: 'High Availability & Load Balancing' +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js new file mode 100644 index 0000000000..f1c1ed1fcc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentsListView from './SoftwareProductComponentsListView.jsx'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductComponents} = softwareProduct; + let {componentsList} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isReadOnlyMode, + componentsList + }; +}; + + +const mapActionToProps = (dispatch, {version}) => { + return { + onComponentSelect: ({id: softwareProductId, componentId}) => { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); + } + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js new file mode 100644 index 0000000000..b91362a0cf --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.COMPONENTS_LIST_UPDATE: + return [...action.componentsList]; + case actionTypes.COMPONENTS_LIST_EDIT: + const indexForEdit = state.findIndex(component => component.id === action.component.id); + return [...state.slice(0, indexForEdit), action.component, ...state.slice(indexForEdit + 1)]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx new file mode 100644 index 0000000000..0c0ba0f646 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx @@ -0,0 +1,89 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +const ComponentPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + displayName: React.PropTypes.string, + description: React.PropTypes.string +}); + +class SoftwareProductComponentsListView extends React.Component { + + state = { + localFilter: '' + }; + + static propTypes = { + isReadOnlyMode: React.PropTypes.bool, + componentsList: React.PropTypes.arrayOf(ComponentPropType), + onComponentSelect: React.PropTypes.func + }; + + render() { + let {componentsList = []} = this.props; + return ( + <div className=''> + { + componentsList.length > 0 && this.renderComponents() + } + </div> + ); + } + + renderComponents() { + const {localFilter} = this.state; + let {isReadOnlyMode} = this.props; + + return ( + <ListEditorView + title={i18n('Virtual Function Components')} + filterValue={localFilter} + placeholder={i18n('Filter Components')} + onFilter={filter => this.setState({localFilter: filter})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(component => this.renderComponentsListItem(component))} + </ListEditorView> + ); + } + + renderComponentsListItem(component) { + let {id: componentId, name, displayName, description = ''} = component; + let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; + return ( + <ListEditorItemView + key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} + className='list-editor-item-view' + onSelect={() => onComponentSelect({id, componentId})}> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Component')}</div> + <div className='name'>{displayName}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> + </ListEditorItemView> + ); + } + + filterList() { + let {componentsList = []} = this.props; + + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return componentsList.filter(({displayName = '', description = ''}) => { + return escape(displayName).match(filter) || escape(description).match(filter); + }); + } + else { + return componentsList; + } + } +} + +export default SoftwareProductComponentsListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js new file mode 100644 index 0000000000..7ac1c707ab --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentComputeView from './SoftwareProductComponentComputeView.jsx'; +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + let minNumberOfVMsSelectedByUser = 0; + if(qdata && qdata.compute && qdata.compute.numOfVMs){ + minNumberOfVMsSelectedByUser = qdata.compute.numOfVMs.minimum || 0; + } + + return { + qdata, + qschema, + isReadOnlyMode, + minNumberOfVMsSelectedByUser + }; +}; + +const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true}) (SoftwareProductComponentComputeView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx new file mode 100644 index 0000000000..3bad147117 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx @@ -0,0 +1,129 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + + +class SoftwareProductComponentComputeView extends React.Component { + + static propTypes = { + qdata: React.PropTypes.object, + qschema: React.PropTypes.object, + isReadOnlyMode: React.PropTypes.bool, + minNumberOfVMsSelectedByUser: React.PropTypes.number, + onQDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired + }; + + render() { + let {qdata, qschema, isReadOnlyMode, minNumberOfVMsSelectedByUser, onQDataChanged, onSubmit} = this.props; + + return ( + <div className='vsp-component-questionnaire-view'> + <ValidationForm + ref='computeValidationForm' + hasButtons={false} + onSubmit={() => onSubmit({qdata})} + className='component-questionnaire-validation-form' + isReadOnlyMode={isReadOnlyMode} + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema}> + + <div className='section-title'>{i18n('VM Sizing')}</div> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Number of CPUs')} + pointer={'/compute/vmSizing/numOfCPUs'}/> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('File System Size (GB)')} + pointer={'/compute/vmSizing/fileSystemSizeGB'}/> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Persistent Storage/Volume Size (GB)')} + pointer={'/compute/vmSizing/persistentStorageVolumeSize'}/> + </div> + <ValidationInput + type='text' + label={i18n('I/O Operations (per second)')} + pointer={'/compute/vmSizing/IOOperationsPerSec'}/> + </div> + </div> + <div className='section-title'>{i18n('Number of VMs')}</div> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Minimum')} + pointer={'/compute/numOfVMs/minimum'}/> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Maximum')} + pointer={'/compute/numOfVMs/maximum'} + validations={{minValue: minNumberOfVMsSelectedByUser}}/> + </div> + <div className='single-col'> + <ValidationInput + type='select' + label={i18n('CPU Oversubscription Ratio')} + pointer={'/compute/numOfVMs/CpuOverSubscriptionRatio'}/> + </div> + <ValidationInput + type='select' + label={i18n('Memory - RAM')} + pointer={'/compute/numOfVMs/MemoryRAM'}/> + </div> + </div> + + <div className='section-title'>{i18n('Guest OS')}</div> + <div className='rows-section'> + <div className='section-field row-flex-components input-row'> + <div className='two-col'> + <ValidationInput + label={i18n('Guest OS')} + type='text' + pointer={'/compute/guestOS/name'}/> + </div> + <div className='empty-two-col'/> + </div> + <div className='vertical-flex input-row'> + <label key='label' className='control-label'>{i18n('OS Bit Size')}</label> + <div className='radio-options-content-row input-row'> + <ValidationInput + type='radiogroup' + pointer={'/compute/guestOS/bitSize'} + className='radio-field'/> + </div> + </div> + <div className='section-field row-flex-components input-row'> + <div className='two-col'> + <ValidationInput + type='textarea' + label={i18n('Guest OS Tools:')} + pointer={'/compute/guestOS/tools'}/> + </div> + <div className='empty-two-col'/> + </div> + </div> + </ValidationForm> + </div> + ); + } + + save(){ + return this.refs.computeValidationForm.handleFormSubmit(new Event('dummy')); + } +} + +export default SoftwareProductComponentComputeView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js new file mode 100644 index 0000000000..e4c330bec8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentsGeneralView from './SoftwareProductComponentsGeneralView.jsx'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {data: componentData = {} , qdata, qschema}} = softwareProductComponents; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + componentData, + qdata, + qschema, + isReadOnlyMode + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}), + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + {softwareProductId, vspComponentId: componentId, componentData, qdata}); + } + }; + +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsGeneralView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx new file mode 100644 index 0000000000..5d11e42cd3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx @@ -0,0 +1,186 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + + +class SoftwareProductComponentsGeneralView extends React.Component { + + render() { + let {qdata, qschema, onQDataChanged, onDataChanged, componentData: {displayName, description}, isReadOnlyMode} = this.props; + return( + <div className='vsp-components-general'> + <div className='general-data'> + <ValidationForm + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + <div className=''> + <h3 className='section-title'>{i18n('General')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + {/** disabled until backend will be ready to implement it + <div className='validation-input-wrapper'> + <div className='form-group'> + <label className='control-label'>{i18n('Name')}</label> + <div>{name}</div> + </div> + </div> + + */} + <div className='single-col'> + <ValidationInput label={i18n('Name')} value={displayName} disabled={true} type='text'/> + </div> + <div className='two-col'> + <ValidationInput + label={i18n('Description')} + onChange={description => onDataChanged({description})} + disabled={isReadOnlyMode} + value={description} + type='textarea'/> + </div> + <div className='empty-col' /> + </div> + </div> + </div> + </ValidationForm> + { + qschema && + <ValidationForm + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + <h3 className='section-title additional-validation-form'>{i18n('Hypervisor')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + label={i18n('Supported Hypervisors')} + type='select' + pointer='/general/hypervisor/hypervisor'/> + </div> + <div className='two-col'> + <ValidationInput + label={i18n('Hypervisor Drivers')} + type='text' + pointer='/general/hypervisor/drivers'/> + </div> + <div className='empty-col' /> + </div> + <div className='row-flex-components input-row'> + <div className='three-col'> + <ValidationInput + label={i18n('Describe Container Features')} + type='textarea' + pointer='/general/hypervisor/containerFeaturesDescription'/> + </div> + <div className='empty-col' /> + </div> + </div> + <h3 className='section-title'>{i18n('Image')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + label={i18n('Image format')} + type='select' + pointer='/general/image/format'/> + </div> + <div className='single-col'> + <ValidationInput + label={i18n('Image provided by')} + type='select' + pointer='/general/image/providedBy'/> + </div> + <div className='single-col'> + <ValidationInput + label={i18n('Size of boot disk per VM (GB)')} + type='text' + pointer='/general/image/bootDiskSizePerVM'/> + </div> + <ValidationInput + label={i18n('Size of ephemeral disk per VM (GB)')} + type='text' + pointer='/general/image/ephemeralDiskSizePerVM'/> + </div> + </div> + <h3 className='section-title'>{i18n('Recovery')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + label={i18n('VM Recovery Point Objective (Minutes)')} + type='text' + pointer='/general/recovery/pointObjective'/> + </div> + <ValidationInput + label={i18n('VM Recovery Time Objective (Minutes)')} + type='text' + pointer='/general/recovery/timeObjective'/> + <div className='empty-two-col' /> + </div> + + + <div className='row-flex-components input-row'> + <div className='two-col'> + <ValidationInput + className='textarea' + label={i18n('How are in VM process failures handled?')} + type='textarea' + pointer='/general/recovery/vmProcessFailuresHandling'/> + </div> + <div className='empty-two-col' /> + { + /** disabled until backend will be ready to implement it + <div className='row'> + <div className='col-md-3'> + <ValidationInput + label={i18n('VM Recovery Document')} + type='text' + pointer='/general/recovery/VMRecoveryDocument'/> + </div> + </div> + */ + } + </div> + </div> + <h3 className='section-title'>{i18n('DNS Configuration')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='two-col'> + <ValidationInput + label={i18n('Do you have a need for DNS as a Service? Please describe.')} + type='textarea' + pointer='/general/dnsConfiguration'/> + </div> + <div className='empty-two-col' /> + </div> + </div> + <h3 className='section-title'>{i18n('Clone')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='two-col'> + <ValidationInput + label={i18n('Describe VM Clone Use')} + type='textarea' + pointer='/general/vmCloneUsage'/> + </div> + <div className='empty-two-col' /> + </div> + </div> + </ValidationForm> + } + </div> + </div> + ); + } + + save() { + let {onSubmit, componentData, qdata} = this.props; + return onSubmit({componentData, qdata}); + } +} + +export default SoftwareProductComponentsGeneralView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js new file mode 100644 index 0000000000..4d4034de5b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentLoadBalancingView from './SoftwareProductComponentLoadBalancingRefView.jsx'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + qdata, + qschema, + isReadOnlyMode + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + }; + +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentLoadBalancingView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx new file mode 100644 index 0000000000..1aa2babc12 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + +const prefix = '/highAvailabilityAndLoadBalancing/'; + +const pointers = [ + { + key: 'failureLoadDistribution', + description: 'How is load distributed across live vms in the event of a vm/host failure? please describe' + }, + { + key: 'nkModelImplementation', + description: 'Does each VM implement the N+K model for redundancy and failure protection? Please describe.' + }, + { + key: 'architectureChoice', + description: 'What architecture is being implemented: ACTIVE-ACTIVE and/or ACTIVE-PASSIVE. ', + added: 'Will the arrangement be 1-1 or N-M? Please describe.' + }, + {key: 'slaRequirements', description: 'Specify application SLA requirements on Cloud platform.'}, + { + key: 'horizontalScaling', + description: 'Is horizontal scaling the preferred solution for HA and resiliency? Please describe.' + }, + { + key: 'loadDistributionMechanism', + description: 'Can load be distributed across VMs? If so, are special mechanisms needed to re-balance data across VMs?', + added: 'Please describe.' + } +]; + +class SoftwareProductComponentLoadBalancingView extends React.Component { + static propTypes = { + componentId: React.PropTypes.string.isRequired, + softwareProductId: React.PropTypes.string.isRequired, + qdata: React.PropTypes.object, + qschema: React.PropTypes.object, + currentSoftwareProduct: React.PropTypes.object + }; + + state = { + expanded: {} + }; + + renderTextAreaItem(item) { + return ( + <div className='question'> + <div className={this.state.expanded[item.key] ? 'title' : 'title add-padding'} + onClick={() => this.toggle(item.key)}> + <FontAwesome name={this.state.expanded[item.key] ? 'chevron-up' : 'chevron-down'}/> + {i18n(item.description)} + {item.added && <div className='new-line'>{i18n(item.added)}</div>} + </div> + <div className={this.state.expanded[item.key] ? 'collapse in' : 'collapse'}> + <div className='row'> + <div className='col-md-9'> + <ValidationInput + type='textarea' + pointer={`${prefix}${item.key}`} + maxLength='1000' /> + </div> + </div> + </div> + </div> + ); + } + + render() { + let {qdata, qschema, onQDataChanged, isReadOnlyMode} = this.props; + return ( + <div className='vsp-components-load-balancing'> + <div className='halb-data'> + <div className='load-balancing-page-title'>{i18n('High Availability & Load Balancing')}</div> + <ValidationForm + onDataChanged={onQDataChanged} + data={qdata} schema={qschema} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + {pointers.map(pointer => this.renderTextAreaItem(pointer))} + </ValidationForm> + </div> + </div> + ); + } + + toggle(name) { + let st = this.state.expanded[name] ? true : false; + let newState = {...this.state}; + newState.expanded[name] = !st; + this.setState(newState); + } + + save() { + let {onSubmit, qdata} = this.props; + return onSubmit({qdata}); + } +} + +export default SoftwareProductComponentLoadBalancingView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js new file mode 100644 index 0000000000..ed7c5a116a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductComponentsMonitoringView from './SoftwareProductComponentsMonitoringView.jsx'; +import SoftwareProductComponentsMonitoringAction from './SoftwareProductComponentsMonitoringActionHelper.js'; + + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentVSP = {}}, softwareProductComponents: {monitoring}} = softwareProduct; + let {trapFilename, pollFilename} = monitoring; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + isReadOnlyMode, + trapFilename, + pollFilename + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onDropMibFileToUpload: (formData, type) => + SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, { + softwareProductId, + componentId, + formData, + type + }), + + onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, { + softwareProductId, + componentId, + type + }) + + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsMonitoringView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js new file mode 100644 index 0000000000..3faf571c09 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import SoftwareProductComponentsMonitoringConstants, {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; + +const UPLOAD = true; + +function baseUrl(vspId, componentId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/components/${componentId}/monitors`; +} + +function snmpTrapUrl(vspId, componentId, isUpload) { + return `${baseUrl(vspId, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; +} + +function snmpPollUrl(vspId, componentId, isUpload) { + return `${baseUrl(vspId, componentId)}/snmp${isUpload ? '/upload' : ''}`; +} + +let onInvalidFileSizeUpload = (dispatch) => dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: { + title: i18n('Upload Failed'), + msg: i18n('no zip file was uploaded or zip file doesn\'t exist') + } +}); + +let uploadSnmpTrapFile = (dispatch, {softwareProductId, componentId, formData}) => { + RestAPIUtil.create(snmpTrapUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({ + type: actionTypes.SNMP_TRAP_UPLOADED, data: {filename: formData.get('upload').name} + })); +}; + +let uploadSnmpPollFile = (dispatch, {softwareProductId, componentId, formData}) => { + RestAPIUtil.create(snmpPollUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({ + type: actionTypes.SNMP_POLL_UPLOADED, data: {filename: formData.get('upload').name} + })); +}; + +let deleteSnmpTrapFile = (dispatch, {softwareProductId, componentId}) => { + RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ + type: actionTypes.SNMP_TRAP_DELETED + })); +}; + +let deleteSnmpPollFile = (dispatch, {softwareProductId, componentId}) => { + RestAPIUtil.destroy(snmpPollUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ + type: actionTypes.SNMP_POLL_DELETED + })); +}; + +const SoftwareProductComponentsMonitoringAction = { + + fetchExistingFiles(dispatch, {softwareProductId, componentId}){ + RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/snmp`).then(response => + dispatch({ + type: actionTypes.SNMP_FILES_DATA_CHANGE, + data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll} + }) + ); + }, + + uploadSnmpFile(dispatch, {softwareProductId, componentId, formData, type}){ + if (formData.get('upload').size) { + if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { + uploadSnmpTrapFile(dispatch, {softwareProductId, componentId, formData}); + } + else { + uploadSnmpPollFile(dispatch, {softwareProductId, componentId, formData}); + } + } + else { + onInvalidFileSizeUpload(dispatch); + } + }, + + deleteSnmpFile(dispatch, {softwareProductId, componentId, type}){ + if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { + deleteSnmpTrapFile(dispatch, {softwareProductId, componentId}); + } + else { + deleteSnmpPollFile(dispatch, {softwareProductId, componentId}); + } + } + +}; + +export default SoftwareProductComponentsMonitoringAction; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js new file mode 100644 index 0000000000..eeececb050 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + + SNMP_FILES_DATA_CHANGE: null, + + SNMP_TRAP_UPLOADED: null, + SNMP_POLL_UPLOADED: null, + + SNMP_TRAP_DELETED: null, + SNMP_POLL_DELETED: null +}); + +export default keyMirror({ + SNMP_TRAP: null, + SNMP_POLL: null +}); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js new file mode 100644 index 0000000000..72e0a85b10 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SNMP_FILES_DATA_CHANGE: + return { + ...state, + trapFilename: action.data.trapFilename, + pollFilename: action.data.pollFilename + }; + case actionTypes.SNMP_TRAP_UPLOADED: + return { + ...state, + trapFilename: action.data.filename + }; + case actionTypes.SNMP_POLL_UPLOADED: + return { + ...state, + pollFilename: action.data.filename + }; + case actionTypes.SNMP_TRAP_DELETED: + return { + ...state, + trapFilename: undefined + }; + case actionTypes.SNMP_POLL_DELETED: + return { + ...state, + pollFilename: undefined + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx new file mode 100644 index 0000000000..ca090c5f2f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -0,0 +1,117 @@ +import React, {Component, PropTypes} from 'react'; +import Dropzone from 'react-dropzone'; +import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; +import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar.js'; +import Button from 'react-bootstrap/lib/Button.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SoftwareProductComponentsMonitoringConstants from './SoftwareProductComponentsMonitoringConstants.js'; + +class SoftwareProductComponentsMonitoringView extends Component { + + static propTypes = { + isReadOnlyMode: PropTypes.bool, + trapFilename: PropTypes.string, + pollFilename: PropTypes.string, + softwareProductId: PropTypes.string, + + onDropMibFileToUpload: PropTypes.func, + onDeleteSnmpFile: PropTypes.func + }; + + state = { + dragging: false + }; + + + render() { + return ( + <div className='vsp-component-monitoring'> + {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_TRAP)} + {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_POLL)} + </div> + ); + } + + renderDropzoneWithType(type) { + let {isReadOnlyMode, trapFilename, pollFilename} = this.props; + let fileName; + if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { + fileName = trapFilename; + } + else { + fileName = pollFilename; + } + let refAndName = `fileInput${type.toString()}`; + let typeDisplayName = this.getFileTypeDisplayName(type); + return ( + <Dropzone + className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`} + onDrop={files => this.handleImport(files, {isReadOnlyMode, type, refAndName})} + onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref={refAndName} + name={refAndName} + accept='.zip' + disabled> + <div className='draggable-wrapper'> + <div className='section-title'>{typeDisplayName}</div> + {fileName ? this.renderUploadedFileName(fileName, type) : this.renderUploadButton(refAndName)} + </div> + </Dropzone> + ); + } + + renderUploadButton(refAndName) { + let {isReadOnlyMode} = this.props; + return ( + <div + className={`software-product-landing-view-top-block-col-upl${isReadOnlyMode ? ' disabled' : ''}`}> + <div className='drag-text'>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <div className='upload-btn primary-btn' onClick={() => this.refs[refAndName].open()}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + ); + } + + renderUploadedFileName(filename, type) { + return ( + <ButtonToolbar> + <ButtonGroup> + <Button disabled>{filename}</Button> + <Button className='delete-button' onClick={()=>this.props.onDeleteSnmpFile(type)}>X</Button> + </ButtonGroup> + </ButtonToolbar> + ); + } + + + handleOnDragEnter(isReadOnlyMode) { + if (!isReadOnlyMode) { + this.setState({dragging: true}); + } + } + + handleImport(files, {isReadOnlyMode, type, refAndName}) { + if (isReadOnlyMode) { + return; + } + + this.setState({dragging: false}); + let file = files[0]; + let formData = new FormData(); + formData.append('upload', file); + this.refs[refAndName].value = ''; + this.props.onDropMibFileToUpload(formData, type); + } + + getFileTypeDisplayName(type) { + return type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP ? 'SNMP Trap' : 'SNMP Poll'; + } + +} + +export default SoftwareProductComponentsMonitoringView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js new file mode 100644 index 0000000000..a412456e13 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; +import SoftwareProductComponentsNICEditorView from './SoftwareProductComponentsNICEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + + let {network: {nicEditor = {}}} = softwareProductComponents; + let {data, qdata, qschema} = nicEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isValidityData, + data, + qdata, + qschema, + isReadOnlyMode + }; + +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onDataChanged: deltaData => SoftwareProductComponentsNetworkActionHelper.updateNICData(dispatch, {deltaData}), + onSubmit: ({data, qdata}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}), + onCancel: () => SoftwareProductComponentsNetworkActionHelper.closeNICEditor(dispatch), + onQDataChanged: ({data}) => SoftwareProductComponentsNetworkActionHelper.updateNICQuestionnaire(dispatch, {data}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentsNICEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js new file mode 100644 index 0000000000..d49f9ccb1e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.NICEditor.OPEN: + return { + ...state, + data: action.nic + }; + case actionTypes.NICEditor.CLOSE: + return {}; + case actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE: + return { + ...state, + qdata: action.payload.qdata || state.qdata, + qschema: action.payload.qschema || state.qschema + }; + case actionTypes.NICEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx new file mode 100644 index 0000000000..7393a835dc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx @@ -0,0 +1,322 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +class SoftwareProductComponentsNetworkEditorView extends React.Component { + + render() { + let {onCancel, isReadOnlyMode} = this.props; + return ( + <ValidationForm + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + className='vsp-components-network-editor'> + {this.renderEditorFields()} + </ValidationForm> + ); + } + + renderEditorFields() { + let {data = {}, qdata = {}, qschema = {}, onQDataChanged, onDataChanged, isReadOnlyMode} = this.props; + let {name, description, networkName} = data; + let netWorkValues = [{ + enum: networkName, + title: networkName + }]; + return( + <div className='editor-data'> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Name')} + value={name} + disabled={true} + type='text'/> + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Purpose of NIC')} + value={description} + onChange={description => onDataChanged({description})} + disabled={isReadOnlyMode} + type='textarea'/> + </div> + </div> + <ValidationForm + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + <div className='row'> + <div className='part-title'>{i18n('Protocols')}</div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Protocols')} + type='select' + pointer='/protocols/protocols'/> + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Protocol with Highest Traffic Profile')} + type='select' + pointer='/protocols/protocolWithHighestTrafficProfile'/> + </div> + </div> + <div className='row'> + <div className='part-title'>{i18n('IP Configuration')}</div> + <div className='col-md-3'> + <ValidationInput + label={i18n('IPv4 Required')} + type='checkbox' + pointer='/ipConfiguration/ipv4Required'/> + </div> + <div className='col-md-9'> + <ValidationInput + label={i18n('IPv6 Required')} + type='checkbox' + pointer='/ipConfiguration/ipv6Required'/> + </div> + </div> + </ValidationForm> + <div className='row'> + <div className='part-title'>{i18n('Network')}</div> + <div className='col-md-2'> + <ValidationInput + label={i18n('Internal')} + disabled + checked={true} + className='network-radio disabled' + type='radio'/> + </div> + <div className='col-md-4'> + <ValidationInput + label={i18n('External')} + disabled + checked={false} + className='network-radio disabled' + type='radio'/> + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Network')} + type='select' + disabled={true} + values={netWorkValues}/> + </div> + </div> + <ValidationForm + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + <div className='row'> + <div className='part-title'>{i18n('Sizing')}</div> + <div className='col-md-12'> + <ValidationInput + label={i18n('Describe Quality of Service')} + type='textarea' + pointer='/sizing/describeQualityOfService'/> + </div> + </div> + + <div className='row'> + <div className='part-title'>{i18n('Inflow Traffic per second')}</div> + </div> + + <div className='row'> + <div className='col-md-6'> + <div className='row'> + <div className='part-title-small'>{i18n('Packets')}</div> + </div> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Peak')} + type='text' + pointer='/sizing/inflowTrafficPerSecond/packets/peak'/> + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Avg')} + type='text' + pointer='/sizing/inflowTrafficPerSecond/packets/avg'/> + </div> + </div> + </div> + <div className='col-md-6'> + <div className='row'> + <div className='part-title-small'>{i18n('Bytes')}</div> + </div> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Peak')} + type='text' + pointer='/sizing/inflowTrafficPerSecond/bytes/peak'/> + + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Avg')} + type='text' + pointer='/sizing/inflowTrafficPerSecond/bytes/avg'/> + </div> + </div> + </div> + </div> + + <div className='row'> + <div className='part-title'>{i18n('Outflow Traffic per second')}</div> + </div> + + <div className='row'> + <div className='col-md-6'> + <div className='row'> + <div className='part-title-small'>{i18n('Packets')}</div> + </div> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Peak')} + type='text' + pointer='/sizing/outflowTrafficPerSecond/packets/peak'/> + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Avg')} + type='text' + pointer='/sizing/outflowTrafficPerSecond/packets/avg'/> + + </div> + </div> + </div> + <div className='col-md-6'> + <div className='row'> + <div className='part-title-small'>{i18n('Bytes')}</div> + </div> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Peak')} + type='text' + pointer='/sizing/outflowTrafficPerSecond/bytes/peak'/> + + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Avg')} + type='text' + pointer='/sizing/outflowTrafficPerSecond/bytes/avg'/> + + </div> + </div> + </div> + </div> + + <div className='row'> + <div className='part-title'>{i18n('Flow Length')}</div> + </div> + + <div className='row'> + <div className='col-md-6'> + <div className='row'> + <div className='part-title-small'>{i18n('Packets')}</div> + </div> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Peak')} + type='text' + pointer='/sizing/flowLength/packets/peak'/> + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Avg')} + type='text' + pointer='/sizing/flowLength/packets/avg'/> + </div> + </div> + </div> + <div className='col-md-6'> + <div className='row'> + <div className='part-title-small'>{i18n('Bytes')}</div> + </div> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + label={i18n('Peak')} + type='text' + pointer='/sizing/flowLength/bytes/peak'/> + + </div> + <div className='col-md-6'> + <ValidationInput + label={i18n('Avg')} + type='text' + pointer='/sizing/flowLength/bytes/avg'/> + </div> + </div> + </div> + </div> + + <div className='row'> + <div className='col-md-9'> + <div className='row'> + <div className='part-title-small'>{i18n('Acceptable Jitter')}</div> + </div> + <div className='row'> + <div className='col-md-4'> + <ValidationInput + label={i18n('Min')} + type='text' + pointer='/sizing/acceptableJitter/mean'/> + </div> + <div className='col-md-4'> + <ValidationInput + label={i18n('Max')} + type='text' + pointer='/sizing/acceptableJitter/max'/> + </div> + <div className='col-md-4'> + <ValidationInput + label={i18n('Var')} + type='text' + pointer='/sizing/acceptableJitter/variable'/> + </div> + </div> + </div> + <div className='col-md-3'> + <div className='row'> + <div className='part-title-small'>{i18n('Acceptable Packet Loss %')}</div> + </div> + <div className='row'> + <div className='col-md-12'> + <ValidationInput + label={i18n('In Percent')} + type='text' + pointer='/sizing/acceptablePacketLoss'/> + </div> + </div> + </div> + </div> + </ValidationForm> + </div> + + ); + } + submit() { + let {data, qdata, onSubmit} = this.props; + onSubmit({data, qdata}); + } +} + +export default SoftwareProductComponentsNetworkEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js new file mode 100644 index 0000000000..bc53e1a7af --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.NIC_LIST_UPDATE: + return [...action.response]; + case actionTypes.NIC_LIST_EDIT: + const indexForEdit = state.findIndex(nic => nic.id === action.nic.id); + return [...state.slice(0, indexForEdit), action.nic, ...state.slice(indexForEdit + 1)]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js new file mode 100644 index 0000000000..8ff6b50189 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js @@ -0,0 +1,129 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; + +function baseUrl(softwareProductId, componentId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics`; +} + + +function fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire${versionQuery}`); +} + +function fetchNIC({softwareProductId, componentId, nicId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}${versionQuery}`); +} + +function fetchNICsList({softwareProductId, componentId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +} + +function saveNIC({softwareProductId, componentId, nic: {id, name, description, networkId}}) { + return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${id}`,{ + name, + description, + networkId + }); +} + +function saveNICQuestionnaire({softwareProductId, componentId, nicId, qdata}) { + return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire`, qdata); +} + +const SoftwareProductComponentNetworkActionHelper = { + + fetchNICsList(dispatch, {softwareProductId, componentId, version}) { + return fetchNICsList({softwareProductId, componentId, version}).then((response) => { + dispatch({ + type: actionTypes.NIC_LIST_UPDATE, + response: response.results + }); + }); + }, + + openNICEditor(dispatch, {nic = {}, data = {}}) { + dispatch({ + type: actionTypes.NICEditor.OPEN, + nic: {...data, id: nic.id} + }); + }, + + closeNICEditor(dispatch) { + dispatch({ + type: actionTypes.NICEditor.CLOSE + }); + }, + + loadNICData({softwareProductId, componentId, nicId, version}) { + return fetchNIC({softwareProductId, componentId, nicId, version}); + }, + + loadNICQuestionnaire(dispatch, {softwareProductId, componentId, nicId, version}) { + return fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}).then((response) => { + dispatch({ + type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, + payload: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + } + }); + }); + }, + + updateNICData(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.NICEditor.DATA_CHANGED, + deltaData + }); + }, + + updateNICQuestionnaire(dispatch, {data}) { + dispatch({ + type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, + payload: { + qdata: data + } + }); + }, + + saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}) { + SoftwareProductComponentNetworkActionHelper.closeNICEditor(dispatch); + return Promise.all([ + saveNICQuestionnaire({softwareProductId, componentId, nicId: data.id, qdata}), + saveNIC({softwareProductId, componentId, nic: data}).then(() => { + dispatch({ + type: actionTypes.NIC_LIST_EDIT, + nic: data + }); + }) + ]); + } +}; + +export default SoftwareProductComponentNetworkActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js new file mode 100644 index 0000000000..193f4b20b5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + NIC_LIST_EDIT: null, + NIC_LIST_UPDATE: null, + + NICEditor: { + OPEN: null, + CLOSE: null, + NIC_QUESTIONNAIRE_UPDATE: null, + DATA_CHANGED: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js new file mode 100644 index 0000000000..9172dc691a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js @@ -0,0 +1,86 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductComponentsNetworkListView from './SoftwareProductComponentsNetworkListView.jsx'; +import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; + + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, qschema}} = softwareProductComponents; + let {data} = nicEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {version} = currentSoftwareProduct; + let manualMode = nicList.length <= 0; + let isModalInEditMode = true; + + return { + version, + componentData, + qdata, + qschema, + isValidityData, + nicList, + isDisplayModal: Boolean(data), + isModalInEditMode, + manualMode, + isReadOnlyMode + }; + +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onAddNIC: () => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch), + onEditNicClick: (nic, version) => { + Promise.all([ + SoftwareProductComponentsNetworkActionHelper.loadNICData({ + softwareProductId, + componentId, + nicId: nic.id, + version + }), + SoftwareProductComponentsNetworkActionHelper.loadNICQuestionnaire(dispatch, { + softwareProductId, + componentId, + nicId: nic.id, + version + }) + ]).then( + ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data}) + ); + }, + onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + {softwareProductId, + vspComponentId: componentId, + qdata}); + } + + + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsNetworkListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx new file mode 100644 index 0000000000..b3e17ff94b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -0,0 +1,136 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js'; + +class SoftwareProductComponentsNetworkView extends React.Component { + + state = { + localFilter: '' + }; + + render() { + let {qdata, qschema, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; + + return( + <div className='vsp-components-network'> + <div className='network-data'> + <div> + <ValidationForm + onDataChanged={onQDataChanged} + data={qdata} + isReadOnlyMode={isReadOnlyMode} + schema={qschema} + hasButtons={false}> + <h3 className='section-title'>{i18n('Network Capacity')}</h3> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + label={i18n('Protocol with Highest Traffic Profile across all NICs')} + type='select' + pointer='/network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'/> + </div> + <div className='single-col add-line-break'> + <ValidationInput + label={i18n('Network Transactions per Second')} + type='text' + pointer='/network/networkCapacity/networkTransactionsPerSecond'/> + </div> + <div className='empty-two-col' /> + </div> + </div> + + </ValidationForm> + </div> + {this.renderNicList()} + </div> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='network-nic-modal'> + <Modal.Header> + <Modal.Title>{isModalInEditMode ? i18n('Edit NIC') : i18n('Create New NIC')}</Modal.Title> + </Modal.Header> + <Modal.Body> + { + <SoftwareProductComponentsNICEditor + softwareProductId={softwareProductId} + componentId={componentId} + isReadOnlyMode={isReadOnlyMode}/> + } + </Modal.Body> + </Modal> + </div> + ); + } + + renderNicList() { + const {localFilter} = this.state; + let {onAddNIC, manualMode, isReadOnlyMode} = this.props; + let onAdd = manualMode ? onAddNIC : false; + return ( + <ListEditorView + title={i18n('Interfaces')} + plusButtonTitle={i18n('Add NIC')} + filterValue={localFilter} + placeholder={i18n('Filter NICs by Name')} + onAdd={onAdd} + isReadOnlyMode={isReadOnlyMode} + onFilter={filter => this.setState({localFilter: filter})}> + {!manualMode && this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} + </ListEditorView> + ); + } + + renderNicListItem(nic, isReadOnlyMode) { + let {id, name, description, networkName = ''} = nic; + let {onEditNicClick, version} = this.props; + return ( + <ListEditorItemView + key={id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditNicClick(nic, version)}> + + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Name')}</div> + <div className='name'>{name}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Purpose of NIC')}</div> + <div className='description'>{description}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Network')}</div> + <div className='artifact-name'>{networkName}</div> + </div> + + </ListEditorItemView> + ); + } + + filterList() { + let {nicList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return nicList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return nicList; + } + } + + save() { + let {onSubmit, qdata} = this.props; + return onSubmit({qdata}); + } +} + +export default SoftwareProductComponentsNetworkView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js new file mode 100644 index 0000000000..d535a34a82 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js @@ -0,0 +1,145 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; + +function baseUrl(softwareProductId, componentId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`; +} + +function fetchProcessesList({softwareProductId, componentId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +} + +function deleteProcess({softwareProductId, componentId, processId}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId)}/${processId}`); +} + +function putProcess({softwareProductId, componentId, process}) { + return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${process.id}`, { + name: process.name, + description: process.description + }); +} + +function postProcess({softwareProductId,componentId, process}) { + return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}`, { + name: process.name, + description: process.description + }); +} + +function uploadFileToProcess({softwareProductId, processId, componentId, formData}) { + return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}/${processId}/upload`, formData); +} + + + +const SoftwareProductComponentProcessesActionHelper = { + fetchProcessesList(dispatch, {softwareProductId, componentId, version}) { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, + processesList: [] + }); + + return fetchProcessesList({softwareProductId, componentId, version}).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, + processesList: response.results + }); + }); + }, + + deleteProcess(dispatch, {process, softwareProductId, componentId}) { + return deleteProcess({softwareProductId, processId:process.id, componentId}).then(() => { + dispatch({ + type: actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, + processId: process.id + }); + }); + + }, + + saveProcess(dispatch, {softwareProductId, componentId, previousProcess, process}) { + if (previousProcess) { + return putProcess({softwareProductId,componentId, process}).then(() => { + if (process.formData && process.formData.name !== previousProcess.artifactName){ + uploadFileToProcess({softwareProductId, processId: process.id, formData: process.formData, componentId}); + } + dispatch({ + type: actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, + process + }); + }); + } + else { + return postProcess({softwareProductId, componentId, process}).then(response => { + if (process.formData) { + uploadFileToProcess({softwareProductId, processId: response.value, formData: process.formData, componentId}); + } + dispatch({ + type: actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, + process: { + ...process, + id: response.value + } + }); + }); + } + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM, + processToDelete: false + }); + }, + + openDeleteProcessesConfirm(dispatch, {process} ) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM, + processToDelete: process + }); + }, + + openEditor(dispatch, process = {}) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN, + process + }); + }, + closeEditor(dispatch) { + dispatch({ + type:actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE + }); + }, + processEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.processEditor.DATA_CHANGED, + deltaData + }); + } +}; + +export default SoftwareProductComponentProcessesActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js new file mode 100644 index 0000000000..78a111a426 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null, + EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null, + DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null, + SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: null, + SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: null, + FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES: null, + SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM: null, + processEditor: { + DATA_CHANGED: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js new file mode 100644 index 0000000000..0138023c30 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper'; +import SoftwareProductComponentProcessesEditorView from './SoftwareProductComponentProcessesEditorView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductComponents: {componentProcesses = {}}} = softwareProduct; + let {processesList = [], processesEditor = {}} = componentProcesses; + let {data} = processesEditor; + + let previousData; + const processId = data ? data.id : null; + if(processId) { + previousData = processesList.find(process => process.id === processId); + } + + return { + data, + previousData + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + + return { + onDataChanged: deltaData => SoftwareProductComponentProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onCancel: () => SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch), + onSubmit: ({previousProcess, process}) => { + SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch); + SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, componentId, process}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentProcessesEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js new file mode 100644 index 0000000000..f859f690e8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: + return { + ...state, + data: action.process + }; + case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: + return {}; + + case actionTypes.processEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx new file mode 100644 index 0000000000..ca6d843af7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx @@ -0,0 +1,124 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Dropzone from 'react-dropzone'; + + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + artifactName: React.PropTypes.string +}); + +class SoftwareProductProcessesEditorView extends React.Component { + + state = { + dragging: false, + files: [] + }; + + static propTypes = { + data: SoftwareProductProcessEditorPropType, + previousData: SoftwareProductProcessEditorPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func, + onSubmit: React.PropTypes.func, + onCancel: React.PropTypes.func + }; + + render() { + let {isReadOnlyMode, onCancel, onDataChanged, data = {}} = this.props; + let {name, description, artifactName} = data; + + return ( + <div> + <ValidationForm + ref='validationForm' + isReadOnlyMode={isReadOnlyMode} + hasButtons={true} + labledButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + className='vsp-processes-editor'> + <div className={`vsp-processes-editor-data${isReadOnlyMode ? ' disabled' : '' }`}> + <Dropzone + className={`vsp-process-dropzone-view ${this.state.dragging ? 'active-dragging' : ''}`} + onDrop={files => this.handleImportSubmit(files)} + onDragEnter={() => this.setState({dragging:true})} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput' + accept='*.*'> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + onChange={name => onDataChanged({name})} + label={i18n('Name')} + value={name} + validations={{validateName: true, maxLength: 120, required: true}} + type='text'/> + <ValidationInput + label={i18n('Artifacts')} + value={artifactName} + type='text' + disabled/> + </div> + <div className='col-md-6'> + <div className='file-upload-box'> + <div className='drag-text'>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <div className='upload-btn primary-btn' onClick={() => this.refs.processEditorFileInput.open()}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + </div> + </div> + <ValidationInput + onChange={description => onDataChanged({description})} + label={i18n('Notes')} + value={description} + name='vsp-process-description' + className='vsp-process-description' + validations={{maxLength: 1000}} + type='textarea'/> + </Dropzone> + </div> + </ValidationForm> + </div> + ); + } + + submit() { + const {data: process, previousData: previousProcess} = this.props; + let {files} = this.state; + let formData = new FormData(); + if (files.length) { + let file = files[0]; + formData.append('upload', file); + } + + let updatedProcess = { + ...process, + formData: files.length ? formData : false + }; + this.props.onSubmit({process: updatedProcess, previousProcess}); + } + + + handleImportSubmit(files) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } +} + +export default SoftwareProductProcessesEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js new file mode 100644 index 0000000000..5f6932897e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; + +import SoftwareProductComponentsProcessesListView from './SoftwareProductComponentsProcessesListView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents: {componentProcesses = {}}} = softwareProduct; + let{processesList = [], processesEditor = {}} = componentProcesses; + let {data} = processesEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isValidityData, + processesList, + isDisplayModal: Boolean(data), + isModalInEditMode: Boolean(data && data.id), + isReadOnlyMode + }; + +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + + return { + onAddProcess: () => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch), + onEditProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch, process), + onDeleteProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsProcessesListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js new file mode 100644 index 0000000000..4bb124d52f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES: + return [...action.processesList]; + case actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: + const indexForEdit = state.findIndex(process => process.id === action.process.id); + return [...state.slice(0, indexForEdit), action.process, ...state.slice(indexForEdit + 1)]; + case actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: + return [...state, action.process]; + case actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: + return state.filter(process => process.id !== action.processId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx new file mode 100644 index 0000000000..48fa862364 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function renderMsg(processToDelete) { + let name = processToDelete ? processToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + return ( + <div> + <p>{msg}</p> + </div> + ); +}; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor, softwareProductComponents} = softwareProduct; + let {componentProcesses} = softwareProductComponents; + let {processToDelete} = componentProcesses; + let softwareProductId = softwareProductEditor.data.id; + const show = processToDelete !== false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: renderMsg(processToDelete), + confirmationDetails: {processToDelete, softwareProductId} + }; +}; + +const mapActionsToProps = (dispatch,{componentId, softwareProductId}) => { + return { + onConfirmed: ({processToDelete}) => { + SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId, componentId}); + SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx new file mode 100644 index 0000000000..a8b07e9194 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx @@ -0,0 +1,125 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import SoftwareProductProcessesEditor from './SoftwareProductComponentProcessesEditor.js'; +import SoftwareProductComponentsProcessesConfirmationModal from './SoftwareProductComponentsProcessesConfirmationModal.jsx'; + +class SoftwareProductProcessesView extends React.Component { + + state = { + localFilter: '' + }; + + static propTypes = { + onAddProcess: React.PropTypes.func, + onEditProcessClick: React.PropTypes.func, + onDeleteProcessClick: React.PropTypes.func, + isDisplayModal: React.PropTypes.bool, + isModalInEditMode: React.PropTypes.bool, + onStorageSelect: React.PropTypes.func, + componentId: React.PropTypes.string, + softwareProductId: React.PropTypes.string + }; + + render() { + let { softwareProductId, componentId} = this.props; + + return ( + <div className='vsp-processes-page'> + <div className='software-product-view'> + <div className='software-product-landing-view-right-side flex-column'> + {this.renderEditor()} + {this.renderProcessList()} + </div> + <SoftwareProductComponentsProcessesConfirmationModal + componentId={componentId} + softwareProductId={softwareProductId}/> + </div> + </div> + ); + } + + renderEditor() { + let {softwareProductId, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + return ( + <Modal show={isDisplayModal} bsSize='large' animation={true}> + <Modal.Header> + <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title> + </Modal.Header> + <Modal.Body className='edit-process-modal'> + <SoftwareProductProcessesEditor + componentId={componentId} + softwareProductId={softwareProductId} + isReadOnlyMode={isReadOnlyMode}/> + </Modal.Body> + </Modal> + + ); + } + + renderProcessList() { + const {localFilter} = this.state; + let {onAddProcess, isReadOnlyMode} = this.props; + return ( + <div className='processes-list'> + <ListEditorView + plusButtonTitle={i18n('Add Component Process Details')} + filterValue={localFilter} + placeholder={i18n('Filter Process')} + onAdd={onAddProcess} + isReadOnlyMode={isReadOnlyMode} + onFilter={filter => this.setState({localFilter: filter})}> + {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} + </ListEditorView> + </div> + ); + } + + renderProcessListItem(process, isReadOnlyMode) { + let {id, name, description, artifactName = ''} = process; + let {onEditProcessClick, onDeleteProcessClick} = this.props; + return ( + <ListEditorItemView + key={id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditProcessClick(process)} + onDelete={() => onDeleteProcessClick(process)}> + + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Name')}</div> + <div className='name'>{name}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Artifact name')}</div> + <div className='artifact-name'>{artifactName}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Notes')}</div> + <div className='description'>{description}</div> + </div> + </ListEditorItemView> + ); + } + + + filterList() { + let {processesList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return processesList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return processesList; + } + } +} + +export default SoftwareProductProcessesView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js new file mode 100644 index 0000000000..fbd3f81ec2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductComponentStorageView from './SoftwareProductComponentStorageView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {data: componentData , qdata, qschema}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + componentData, + qdata, + qschema, + isReadOnlyMode + }; +}; + +const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true}) (SoftwareProductComponentStorageView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx new file mode 100644 index 0000000000..9c9600c376 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx @@ -0,0 +1,124 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + + +class SoftwareProductComponentStorageView extends React.Component { + + static propTypes = { + componentId: React.PropTypes.string, + onQDataChanged: React.PropTypes.func, + onSubmit: React.PropTypes.func, + isReadOnlyMode: React.PropTypes.bool + }; + + render() { + let {qdata, qschema, onQDataChanged, onSubmit, isReadOnlyMode} = this.props; + + return( + <div className='vsp-component-questionnaire-view'> + <ValidationForm + ref='storageValidationForm' + hasButtons={false} + onSubmit={() => onSubmit({qdata})} + className='component-questionnaire-validation-form' + isReadOnlyMode={isReadOnlyMode} + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema}> + + <div className='section-title'>{i18n('Backup')}</div> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <div className='vertical-flex'> + <label key='label' className='control-label'>{i18n('Backup Type')}</label> + <div className='radio-options-content-row'> + <ValidationInput + label={i18n('On Site')} + type='radiogroup' + pointer={'/storage/backup/backupType'} + className='radio-field'/> + </div> + </div> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Backup Solution')} + pointer={'/storage/backup/backupSolution'} + className='section-field'/> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Backup Storage Size (GB)')} + pointer={'/storage/backup/backupStorageSize'} + className='section-field'/> + </div> + <ValidationInput + type='select' + label={i18n('Backup NIC')} + pointer={'/storage/backup/backupNIC'} + className='section-field'/> + </div> + </div> + + <div className='section-title'>{i18n('Snapshot Backup')}</div> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Snapshot Frequency (hours)')} + pointer={'/storage/snapshotBackup/snapshotFrequency'} + className='section-field'/> + </div> + <div className='empty-two-col' /> + <div className='empty-col' /> + </div> + </div> + + <div className='section-title'>{i18n('Log Backup')}</div> + <div className='rows-section'> + <div className='row-flex-components input-row'> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Size of Log Files (GB)')} + pointer={'/storage/logBackup/sizeOfLogFiles'} + className='section-field'/> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Log Retention Period (days)')} + pointer={'/storage/logBackup/logRetentionPeriod'} + className='section-field'/> + </div> + <div className='single-col'> + <ValidationInput + type='text' + label={i18n('Log Backup Frequency (days)')} + pointer={'/storage/logBackup/logBackupFrequency'} + className='section-field'/> + </div> + <ValidationInput + type='text' + label={i18n('Log File Location')} + pointer={'/storage/logBackup/logFileLocation'} + className='section-field'/> + </div> + </div> + </ValidationForm> + </div> + ); + } + + save(){ + return this.refs.storageValidationForm.handleFormSubmit(new Event('dummy')); + } +} + +export default SoftwareProductComponentStorageView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js new file mode 100644 index 0000000000..46308f0045 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import SoftwareProductCreationActionHelper from './SoftwareProductCreationActionHelper.js'; +import SoftwareProductCreationView from './SoftwareProductCreationView.jsx'; + +const mapStateToProps = ({finalizedLicenseModelList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => { + return { + data: softwareProductCreation.data, + softwareProductCategories, + finalizedLicenseModelList + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => SoftwareProductCreationActionHelper.changeData(dispatch, {deltaData}), + onCancel: () => SoftwareProductCreationActionHelper.resetData(dispatch), + onSubmit: (softwareProduct) => { + SoftwareProductCreationActionHelper.resetData(dispatch); + SoftwareProductCreationActionHelper.createSoftwareProduct(dispatch, {softwareProduct}).then(softwareProductId => { + let {vendorId: licenseModelId, licensingVersion} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion}); + }); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js new file mode 100644 index 0000000000..f4e51f198e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -0,0 +1,77 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import {actionTypes} from './SoftwareProductCreationConstants.js'; + + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/`; +} + +function createSoftwareProduct(softwareProduct) { + return RestAPIUtil.create(baseUrl(), { + ...softwareProduct, + icon: 'icon', + licensingData: {} + }); +} + +const SoftwareProductCreationActionHelper = { + + open(dispatch) { + SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); + dispatch({ + type: actionTypes.OPEN + }); + }, + + resetData(dispatch) { + dispatch({ + type: actionTypes.RESET_DATA + }); + }, + + changeData(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.DATA_CHANGED, + deltaData + }); + }, + + createSoftwareProduct(dispatch, {softwareProduct}) { + return createSoftwareProduct(softwareProduct).then(response => { + SoftwareProductActionHelper.addSoftwareProduct(dispatch, { + softwareProduct: { + ...softwareProduct, + id: response.vspId + } + }); + return response.vspId; + }); + } + +}; + +export default SoftwareProductCreationActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js new file mode 100644 index 0000000000..0a9cdb911c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + OPEN: null, + RESET_DATA: null, + DATA_CHANGED: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js new file mode 100644 index 0000000000..5e3db09e56 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductCreationConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.OPEN: + return { + ...state, + data: {}, + showModal: true + }; + case actionTypes.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.RESET_DATA: + return {}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx new file mode 100644 index 0000000000..2c8f243457 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx @@ -0,0 +1,123 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; + +import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; + + +const SoftwareProductPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + category: React.PropTypes.string, + subCategory: React.PropTypes.string, + vendorId: React.PropTypes.string +}); + +class SoftwareProductCreationView extends React.Component { + + static propTypes = { + data: SoftwareProductPropType, + finalizedLicenseModelList: React.PropTypes.array, + softwareProductCategories: React.PropTypes.array, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {softwareProductCategories, data = {}, onDataChanged, onCancel} = this.props; + let {name, description, vendorId, subCategory} = data; + + const vendorList = this.getVendorList(); + + return ( + <div className='software-product-creation-page'> + <ValidationForm + ref='validationForm' + hasButtons={true} + onSubmit={() => this.submit() } + onReset={() => onCancel() } + labledButtons={true}> + <div className='software-product-form-row'> + <div className='software-product-inline-section'> + <ValidationInput + value={name} + label={i18n('Name')} + ref='software-product-name' + onChange={name => onDataChanged({name})} + validations={{validateName: true, maxLength: 25, required: true}} + type='text' + className='field-section'/> + <ValidationInput + onEnumChange={vendorId => onDataChanged({vendorId})} + value={vendorId} + label={i18n('Vendor')} + values={vendorList} + validations={{required: true}} + type='select' + className='field-section'/> + <ValidationInput + label={i18n('Category')} + type='select' + value={subCategory} + onChange={subCategory => this.onSelectSubCategory(subCategory)} + validations={{required: true}} + className='options-input-category'> + <option key='' value=''>{i18n('please select…')}</option> + {softwareProductCategories.map(category => + category.subcategories && + <optgroup + key={category.name} + label={category.name}>{category.subcategories.map(sub => + <option key={sub.uniqueId} value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)} + </optgroup>) + } + </ValidationInput> + </div> + <div className='software-product-inline-section'> + <ValidationInput + value={description} + label={i18n('Description')} + ref='description' + onChange={description => onDataChanged({description})} + validations={{freeEnglishText: true, maxLength: 1000, required: true}} + type='textarea' + className='field-section'/> + </div> + </div> + </ValidationForm> + </div> + ); + } + + getVendorList() { + let {finalizedLicenseModelList} = this.props; + + return [{enum: '', title: i18n('please select...')}].concat(finalizedLicenseModelList.map(vendor => { + return { + enum: vendor.id, + title: vendor.vendorName + }; + })); + } + + onSelectSubCategory(subCategory) { + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory}); + } + + create(){ + this.refs.validationForm.handleFormSubmit(new Event('dummy')); + } + + submit() { + const {data:softwareProduct, finalizedLicenseModelList} = this.props; + softwareProduct.vendorName = finalizedLicenseModelList.find(vendor => vendor.id === softwareProduct.vendorId).vendorName; + this.props.onSubmit(softwareProduct); + } +} + +export default SoftwareProductCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js new file mode 100644 index 0000000000..16a100c664 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import SoftwareProductDetailsView from './SoftwareProductDetailsView.jsx'; + +export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, licenseModel: {licenseAgreement, featureGroup}}) => { + let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct; + let {licensingData = {}, licensingVersion} = currentSoftwareProduct; + let licenseAgreementList = [], filteredFeatureGroupsList = []; + if(licensingVersion && licensingVersion !== '') { + licenseAgreementList = licenseAgreement.licenseAgreementList; + let selectedLicenseAgreement = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); + if (selectedLicenseAgreement) { + let featureGroupsList = featureGroup.featureGroupsList.filter(({referencingLicenseAgreements}) => referencingLicenseAgreements.includes(selectedLicenseAgreement.id)); + if (featureGroupsList.length) { + filteredFeatureGroupsList = featureGroupsList.map(featureGroup => ({enum: featureGroup.id, title: featureGroup.name})); + } + } + } + let {qdata, qschema} = softwareProductQuestionnaire; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + softwareProductCategories, + licenseAgreementList, + featureGroupsList: filteredFeatureGroupsList, + finalizedLicenseModelList, + qdata, + qschema, + isReadOnlyMode + }; +}; + +export const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}), + onVendorParamChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData}), + onQDataChanged: ({data}) => SoftwareProductActionHelper.softwareProductQuestionnaireUpdate(dispatch, {data}), + onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}), + onSubmit: (softwareProduct, qdata) =>{ return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});} + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductDetailsView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js new file mode 100644 index 0000000000..e060706b37 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.softwareProductEditor.OPEN: + return { + ...state, + data: {} + }; + case actionTypes.softwareProductEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION: + return { + ...state, + uploadData:action.uploadData + }; + case actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED: + return { + ...state, + isValidityData: action.isValidityData + }; + case actionTypes.softwareProductEditor.CLOSE: + return {}; + case actionTypes.SOFTWARE_PRODUCT_LOADED: + return { + ...state, + data: action.response + }; + case actionTypes.TOGGLE_NAVIGATION_ITEM: + return { + ...state, + mapOfExpandedIds: action.mapOfExpandedIds + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx new file mode 100644 index 0000000000..75a5797dec --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx @@ -0,0 +1,264 @@ +import React, {Component, PropTypes} from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; + +class SoftwareProductDetails extends Component { + + static propTypes = { + vendorName: PropTypes.string, + currentSoftwareProduct: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + description: PropTypes.string, + category: PropTypes.string, + subCategory: PropTypes.string, + vendorId: PropTypes.string, + vendorName: PropTypes.string, + licensingVersion: PropTypes.string, + licensingData: PropTypes.shape({ + licenceAgreement: PropTypes.string, + featureGroups: PropTypes.array + }) + }), + softwareProductCategories: PropTypes.array, + finalizedLicenseModelList: PropTypes.array, + licenseAgreementList: PropTypes.array, + featureGroupsList: PropTypes.array, + onSubmit: PropTypes.func.isRequired, + onDataChanged: PropTypes.func.isRequired, + onValidityChanged: PropTypes.func.isRequired, + qdata: PropTypes.object.isRequired, + qschema: PropTypes.object.isRequired, + onQDataChanged: PropTypes.func.isRequired, + onVendorParamChanged: PropTypes.func.isRequired + }; + + state = { + licensingVersionsList: [] + }; + + render() { + let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, featureGroupsList, licenseAgreementList, currentSoftwareProduct} = this.props; + let {name, description, vendorId, licensingVersion, subCategory, licensingData = {}} = currentSoftwareProduct; + let licensingVersionsList = this.state.licensingVersionsList.length > 0 ? this.state.licensingVersionsList : this.refreshVendorVersionsList(vendorId); + let {qdata, qschema, onQDataChanged} = this.props; + let {isReadOnlyMode} = this.props; + + return ( + <div className='vsp-details-page'> + <Form + ref='validationForm' + className='vsp-general-tab' + hasButtons={false} + onSubmit={() => this.props.onSubmit(currentSoftwareProduct, qdata)} + onValidityChanged={(isValidityData) => this.props.onValidityChanged(isValidityData)} + isReadOnlyMode={isReadOnlyMode}> + <div className='section-title general'>{i18n('General')}</div> + <div className='vsp-general-tab-inline-section'> + <div className='vsp-general-tab-sub-section'> + <ValidationInput + label={i18n('Name')} + type='text' + value={name} + onChange={name => onDataChanged({name})} + validations={{validateName: true, maxLength: 120, required: true}} + className='field-section'/> + <ValidationInput + label={i18n('Vendor')} + type='select' + selectedEnum={vendorId} + onEnumChange={vendorId => this.onVendorParamChanged({vendorId})} + className='field-section'> + {finalizedLicenseModelList.map(lm => <option key={lm.id} value={lm.id}>{lm.vendorName}</option>)} + </ValidationInput> + <div className='input-row'> + <ValidationInput + label={i18n('Category')} + type='select' + selectedEnum={subCategory} + onEnumChange={subCategory => this.onSelectSubCategory(subCategory)} + className='field-section'> + { + softwareProductCategories.map(category => + category.subcategories && + <optgroup + key={category.name} + label={category.name}>{category.subcategories.map(sub => + <option + key={sub.uniqueId} + value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)} + </optgroup> + ) + } + </ValidationInput> + </div> + </div> + <div className='vsp-general-tab-sub-section input-row'> + <ValidationInput + label={i18n('Description')} + type='textarea' + value={description} + onChange={description => onDataChanged({description})} + className='field-section' + validations={{required: true}}/> + </div> + </div> + <div className='vsp-general-tab-section licenses'> + <div className='section-title'>{i18n('Licenses')}</div> + <div className='vsp-general-tab-inline-section input-row'> + <ValidationInput + onEnumChange={licensingVersion => this.onVendorParamChanged({vendorId, licensingVersion})} + selectedEnum={licensingVersion} + label={i18n('Licensing Version')} + values={licensingVersionsList} + type='select' + className='field-section'/> + <ValidationInput + label={i18n('License Agreement')} + type='select' + selectedEnum={licensingData.licenseAgreement} + className='field-section' + onEnumChange={(licenseAgreement) => this.onLicensingDataChanged({licenseAgreement, featureGroups: []})}> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {licenseAgreementList.map(la => <option value={la.id} key={la.id}>{la.name}</option>)} + </ValidationInput> + </div> + <div className='vsp-general-tab-inline-section input-row'> + {licensingData.licenseAgreement && ( + <ValidationInput + type='select' + isMultiSelect={true} + onEnumChange={featureGroups => this.onFeatureGroupsChanged({featureGroups})} + multiSelectedEnum={licensingData.featureGroups} + name='feature-groups' + label={i18n('Feature Groups')} + clearable={false} + values={featureGroupsList}/>) + } + </div> + </div> + </Form> + <Form + data={qdata} + schema={qschema} + onDataChanged={onQDataChanged} + className='vsp-general-tab' + hasButtons={false} + isReadOnlyMode={isReadOnlyMode}> + <div className='vsp-general-tab-section'> + <div className='section-title'> {i18n('Availability')} </div> + <div className='vsp-general-tab-inline-section'> + <div className='vsp-general-tab-sub-section input-row'> + <ValidationInput + label={i18n('Use Availability Zones for High Availability')} + type='checkbox' + pointer='/general/availability/useAvailabilityZonesForHighAvailability'/> + </div> + </div> + <div className='section-title'> {i18n('Regions')} </div> + <div className='vsp-general-tab-inline-section'> + <div className='vsp-general-tab-sub-section input-row'> + <ValidationInput + type='select' + laebl='Ziv' + pointer='/general/regionsData/regions'/> + </div> + </div> + <div className='section-title'> {i18n('Storage Data Replication')} </div> + <div className='vsp-general-tab-inline-section'> + <div className='vsp-general-tab-sub-section'> + <ValidationInput + label={i18n('Storage Replication Size (GB)')} + type='text' + pointer='/general/storageDataReplication/storageReplicationSize' + className='field-section'/> + <ValidationInput + label={i18n('Storage Replication Source')} + type='text' + pointer='/general/storageDataReplication/storageReplicationSource' + className='field-section'/> + </div> + <div className='vsp-general-tab-sub-section'> + <ValidationInput + label={i18n('Storage Replication Frequency (minutes)')} + type='text' + pointer='/general/storageDataReplication/storageReplicationFrequency' + className='field-section'/> + <ValidationInput + label={i18n('Storage Replication Destination')} + type='text' + pointer='/general/storageDataReplication/storageReplicationDestination' + className='field-section'/> + </div> + </div> + </div> + </Form> + </div> + ); + } + + onVendorParamChanged({vendorId, licensingVersion}) { + let {finalizedLicenseModelList, onVendorParamChanged} = this.props; + if(!licensingVersion) { + const licensingVersionsList = this.refreshVendorVersionsList(vendorId); + licensingVersion = licensingVersionsList.length > 0 ? licensingVersionsList[0].enum : ''; + } + let vendorName = finalizedLicenseModelList.find(licenseModelItem => licenseModelItem.id === vendorId).vendorName || ''; + let deltaData = { + vendorId, + vendorName, + licensingVersion, + licensingData: {} + }; + onVendorParamChanged(deltaData); + } + + refreshVendorVersionsList(vendorId) { + if(!vendorId) { + return []; + } + + let {finalVersions} = this.props.finalizedLicenseModelList.find(vendor => vendor.id === vendorId); + + let licensingVersionsList = [{ + enum: '', + title: i18n('Select...') + }]; + if(finalVersions) { + finalVersions.forEach(version => licensingVersionsList.push({ + enum: version, + title: version + })); + } + + return licensingVersionsList; + } + + onSelectSubCategory(subCategory) { + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory}); + } + + onFeatureGroupsChanged({featureGroups}) { + this.onLicensingDataChanged({featureGroups}); + } + + onLicensingDataChanged(deltaData) { + this.props.onDataChanged({ + licensingData: { + ...this.props.currentSoftwareProduct.licensingData, + ...deltaData + } + }); + } + + save(){ + return this.refs.validationForm.handleFormSubmit(new Event('dummy')); + } +} + +export default SoftwareProductDetails; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js new file mode 100644 index 0000000000..7604f5841d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js @@ -0,0 +1,97 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import LandingPageView from './SoftwareProductLandingPageView.jsx'; + +const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { + let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct; + let {licensingData = {}} = currentSoftwareProduct; + let {licenseAgreementList} = licenseAgreement; + let {componentsList} = softwareProductComponents; + let licenseAgreementName = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); + if (licenseAgreementName) { + licenseAgreementName = licenseAgreementName.name; + } + + let categoryName = '', subCategoryName = '', fullCategoryDisplayName = ''; + const category = softwareProductCategories.find(ca => ca.uniqueId === currentSoftwareProduct.category); + if (category) { + categoryName = category.name; + const subcategories = category.subcategories || []; + const subcat = subcategories.find(sc => sc.uniqueId === currentSoftwareProduct.subCategory); + subCategoryName = subcat && subcat.name ? subcat.name : ''; + } + fullCategoryDisplayName = `${subCategoryName} (${categoryName})`; + + const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct: { + ...currentSoftwareProduct, + licenseAgreementName, + fullCategoryDisplayName + }, + isReadOnlyMode, + componentsList + }; +}; + +const mapActionsToProps = (dispatch, {version}) => { + return { + onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, { + softwareProductId, + licenseModelId + }), + onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}), + onUpload: (softwareProductId, formData) => + SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed') + }), + onUploadConfirmation: (softwareProductId, formData) => + SoftwareProductActionHelper.uploadConfirmation(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed')}), + + onInvalidFileSizeUpload: () => dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: { + title: i18n('Upload Failed'), + msg: i18n('no zip file was uploaded or zip file doesn\'t exist') + } + }), + onComponentSelect: ({id: softwareProductId, componentId}) => { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); + }, + /** for the next version */ + onAddComponent: () => SoftwareProductActionHelper.addComponent(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(LandingPageView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx new file mode 100644 index 0000000000..4a848834b2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx @@ -0,0 +1,38 @@ +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor} = softwareProduct; + let {uploadData} = softwareProductEditor; + const show = uploadData ? true : false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: i18n('Upload will erase existing data. Do you want to continue?'), + confirmationDetails: {uploadData} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({uploadData}) => { + let {softwareProductId, formData, failedNotificationTitle} = uploadData; + SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle + }); + SoftwareProductActionHelper.hideUploadConfirm(dispatch); + }, + onDeclined: () => { + SoftwareProductActionHelper.hideUploadConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx new file mode 100644 index 0000000000..cf7c7a31a5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -0,0 +1,272 @@ +import React from 'react'; +import classnames from 'classnames'; +import Dropzone from 'react-dropzone'; + + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import FontAwesome from 'react-fontawesome'; +import SoftwareProductLandingPageUploadConfirmationModal from './SoftwareProductLandingPageUploadConfirmationModal.jsx'; + + +const SoftwareProductPropType = React.PropTypes.shape({ + name: React.PropTypes.string, + description: React.PropTypes.string, + version: React.PropTypes.string, + id: React.PropTypes.string, + categoryId: React.PropTypes.string, + vendorId: React.PropTypes.string, + status: React.PropTypes.string, + licensingData: React.PropTypes.object, + validationData: React.PropTypes.object +}); + +const ComponentPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + displayName: React.PropTypes.string, + description: React.PropTypes.string +}); + +class SoftwareProductLandingPageView extends React.Component { + + state = { + localFilter: '', + fileName: '', + dragging: false, + files: [] + }; + + static propTypes = { + currentSoftwareProduct: SoftwareProductPropType, + isReadOnlyMode: React.PropTypes.bool, + componentsList: React.PropTypes.arrayOf(ComponentPropType), + onDetailsSelect: React.PropTypes.func, + onAttachmentsSelect: React.PropTypes.func, + onUpload: React.PropTypes.func, + onUploadConfirmation: React.PropTypes.func, + onInvalidFileSizeUpload: React.PropTypes.func, + onComponentSelect: React.PropTypes.func, + onAddComponent: React.PropTypes.func + }; + + render() { + let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props; + return ( + <div className='software-product-landing-wrapper'> + <Dropzone + className={classnames('software-product-landing-view', {'active-dragging': this.state.dragging})} + onDrop={files => this.handleImportSubmit(files, isReadOnlyMode)} + onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='fileInput' + name='fileInput' + accept='.zip' + disabled> + <div className='draggable-wrapper'> + <div className='software-product-landing-view-top'> + <div className='row'> + {this.renderProductSummary(currentSoftwareProduct)} + {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)} + </div> + </div> + </div> + </Dropzone> + { + componentsList.length > 0 && this.renderComponents() + } + <SoftwareProductLandingPageUploadConfirmationModal confirmationButtonText={i18n('Continue')}/> + </div> + ); + } + + handleOnDragEnter(isReadOnlyMode) { + if (!isReadOnlyMode) { + this.setState({dragging: true}); + } + } + + renderProductSummary(currentSoftwareProduct) { + let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct; + let {onDetailsSelect} = this.props; + return ( + <div className='details-panel'> + <div className='software-product-landing-view-heading-title'>{i18n('Software Product Details')}</div> + <div + className='software-product-landing-view-top-block clickable' + onClick={() => onDetailsSelect(currentSoftwareProduct)}> + <div className='details-container'> + <div className='single-detail-section title-section'> + <div> + <div>{name}</div> + </div> + </div> + <div className='multiple-details-section'> + <div className='detail-col' > + <div className='title'>{i18n('Vendor')}</div> + <div className='description'>{vendorName}</div> + </div> + <div className='detail-col'> + <div className='title'>{i18n('Category')}</div> + <div className='description'>{fullCategoryDisplayName}</div> + </div> + <div className='detail-col'> + <div className='title extra-large'>{i18n('License Agreement')}</div> + <div className='description'> + {this.renderLicenseAgreement(licenseAgreementName)} + </div> + </div> + </div> + <div className='single-detail-section'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> + </div> + </div> + </div> + ); + } + + renderProductDetails(currentSoftwareProduct, isReadOnlyMode) { + let {validationData} = currentSoftwareProduct; + let {onAttachmentsSelect} = this.props; + let details = { + heatTemplates: validationData ? '1' : '0', + images: '0', + otherArtifacts: '0' + }; + + return ( + <div className='details-panel'> + <div className='software-product-landing-view-heading-title'>{i18n('Software Product Attachments')}</div> + <div className='software-product-landing-view-top-block'> + <div + className='software-product-landing-view-top-block-col' + onClick={() => onAttachmentsSelect(currentSoftwareProduct)}> + <div> + <div className='attachment-details'>{i18n('HEAT Templates')} (<span + className='attachment-details-count'>{details.heatTemplates}</span>) + </div> + <div className='attachment-details'>{i18n('Images')} (<span + className='attachment-details-count'>{details.images}</span>) + </div> + <div className='attachment-details'>{i18n('Other Artifacts')} (<span + className='attachment-details-count'>{details.otherArtifacts}</span>) + </div> + </div> + </div> + <div + className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})}> + <div className='drag-text'>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <div className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + </div> + </div> + ); + } + + renderComponents() { + const {localFilter} = this.state; + + return ( + <ListEditorView + title={i18n('Virtual Function Components')} + filterValue={localFilter} + placeholder={i18n('Filter Components')} + onFilter={filter => this.setState({localFilter: filter})}> + {this.filterList().map(component => this.renderComponentsListItem(component))} + </ListEditorView> + ); + } + + renderComponentsListItem(component) { + let {id: componentId, name, displayName, description = ''} = component; + let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; + return ( + <ListEditorItemView + key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} + className='list-editor-item-view' + onSelect={() => onComponentSelect({id, componentId})}> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Component')}</div> + <div className='name'>{displayName}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> + </ListEditorItemView> + ); + } + + renderLicenseAgreement(licenseAgreementName) { + if (!licenseAgreementName) { + return (<FontAwesome name='exclamation-triangle' className='warning-icon'/>); + } + return (licenseAgreementName); + } + + + filterList() { + let {componentsList = []} = this.props; + + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return componentsList.filter(({displayName = '', description = ''}) => { + return escape(displayName).match(filter) || escape(description).match(filter); + }); + } + else { + return componentsList; + } + } + + handleImportSubmit(files, isReadOnlyMode) { + if (isReadOnlyMode) { + return; + } + if (files[0] && files[0].size) { + this.setState({ + fileName: files[0].name, + dragging: false, + complete: '0', + }); + this.startUploading(files); + } + else { + this.props.onInvalidFileSizeUpload(); + } + + } + + startUploading(files) { + let {onUpload, currentSoftwareProduct, onUploadConfirmation} = this.props; + + let {validationData} = currentSoftwareProduct; + + if (!(files && files.length)) { + return; + } + let file = files[0]; + let formData = new FormData(); + formData.append('upload', file); + this.refs.fileInput.value = ''; + + if (validationData) { + onUploadConfirmation(currentSoftwareProduct.id, formData); + }else { + onUpload(currentSoftwareProduct.id, formData); + } + + } +} + +export default SoftwareProductLandingPageView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js new file mode 100644 index 0000000000..dadc7777e1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductNetworksView from './SoftwareProductNetworksView.jsx'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductNetworks: {networksList = []}} = softwareProduct; + return {networksList}; +}; + +export default connect(mapStateToProps, null, null, {withRef: true})(SoftwareProductNetworksView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js new file mode 100644 index 0000000000..d0e29bcfe5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductNetworksConstants.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +function baseUrl(svpId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${svpId}/networks`; +} + + +function fetchNetworksList(softwareProductId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); +} + +const SoftwareProductNetworksActionHelper = { + fetchNetworksList(dispatch, {softwareProductId, version}) { + return fetchNetworksList(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_NETWORKS, + networksList: response.results + }); + }); + } +}; + +export default SoftwareProductNetworksActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js new file mode 100644 index 0000000000..d428d21a26 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js @@ -0,0 +1,25 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + FETCH_SOFTWARE_PRODUCT_NETWORKS: null, +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js new file mode 100644 index 0000000000..0c9c62372a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductNetworksConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_NETWORKS: + return [...action.networksList]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx new file mode 100644 index 0000000000..bd47467fe1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx @@ -0,0 +1,73 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +class SoftwareProductNetworksView extends React.Component { + + static propTypes = { + networksList: React.PropTypes.arrayOf(React.PropTypes.shape({ + id: React.PropTypes.string.isRequired, + name: React.PropTypes.string.isRequired, + dhcp: React.PropTypes.bool.isRequired + })).isRequired + }; + + state = { + localFilter: '' + }; + + render() { + const {localFilter} = this.state; + + return ( + <div className='vsp-networks-page'> + <ListEditorView + title={i18n('Networks')} + filterValue={localFilter} + placeholder={i18n('Filter Networks')} + onFilter={filter => this.setState({localFilter: filter})}> + {this.filterList().map(network => this.renderNetworksListItem(network))} + </ListEditorView> + </div> + ); + } + + renderNetworksListItem(network) { + let {id, name, dhcp} = network; + return ( + <ListEditorItemView + key={id} + className='list-editor-item-view' + isReadOnlyMode={true}> + + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Name')}</div> + <div className='name'>{name}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('DHCP')}</div> + <div className='artifact-name'>{dhcp ? i18n('YES') : i18n('NO')}</div> + </div> + </ListEditorItemView> + ); + } + + filterList() { + let {networksList} = this.props; + + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return networksList.filter(({name = ''}) => { + return escape(name).match(filter); + }); + } + else { + return networksList; + } + } +} + +export default SoftwareProductNetworksView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js new file mode 100644 index 0000000000..5c3a8dae01 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; +import SoftwareProductProcessesView from './SoftwareProductProcessesView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {data} = processesEditor; + + return { + currentSoftwareProduct, + processesList, + isDisplayEditor: Boolean(data), + isModalInEditMode: Boolean(data && data.id), + isReadOnlyMode + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + return { + onAddProcess: () => SoftwareProductProcessesActionHelper.openEditor(dispatch), + onEditProcess: (process) => SoftwareProductProcessesActionHelper.openEditor(dispatch, process), + onDeleteProcess: (process) => SoftwareProductProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductProcessesView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js new file mode 100644 index 0000000000..df5d08ffe5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js @@ -0,0 +1,151 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductProcessesConstants.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +function baseUrl(svpId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${svpId}/processes`; +} + +function putProcess(softwareProductId, process) { + return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${process.id}`, { + name: process.name, + description: process.description + }); +} + +function postProcess(softwareProductId, process) { + return RestAPIUtil.create(`${baseUrl(softwareProductId)}`, { + name: process.name, + description: process.description + }); +} + +function deleteProcess(softwareProductId, processId) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId)}/${processId}`); +} + +function uploadFileToProcess(softwareProductId, processId, formData) +{ + return RestAPIUtil.create(`${baseUrl(softwareProductId)}/${processId}/upload`, formData); +} + +function fetchProcesses(softwareProductId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); +} + + + +const SoftwareProductActionHelper = { + + fetchProcessesList(dispatch, {softwareProductId, version}) { + + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES, + processesList: [] + }); + + return fetchProcesses(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES, + processesList: response.results + }); + }); + }, + openEditor(dispatch, process = {}) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN, + process + }); + }, + + deleteProcess(dispatch, {process, softwareProductId}) { + return deleteProcess(softwareProductId, process.id).then(() => { + dispatch({ + type: actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS, + processId: process.id + }); + }); + + }, + + closeEditor(dispatch) { + dispatch({ + type:actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE + }); + }, + + processEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.processEditor.DATA_CHANGED, + deltaData + }); + }, + + saveProcess(dispatch, {softwareProductId, previousProcess, process}) { + if (previousProcess) { + return putProcess(softwareProductId, process).then(() => { + if (process.formData){ + uploadFileToProcess(softwareProductId, process.id, process.formData); + } + dispatch({ + type: actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS, + process + }); + }); + } + else { + return postProcess(softwareProductId, process).then(response => { + if (process.formData) { + uploadFileToProcess(softwareProductId, response.value, process.formData); + } + dispatch({ + type: actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS, + process: { + ...process, + id: response.value + } + }); + }); + } + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM, + processToDelete: false + }); + }, + + openDeleteProcessesConfirm(dispatch, {process} ) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM, + processToDelete: process + }); + } + +}; + +export default SoftwareProductActionHelper; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx new file mode 100644 index 0000000000..0159352dae --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; + +function renderMsg(processToDelete) { + let name = processToDelete ? processToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + return ( + <div> + <p>{msg}</p> + </div> + ); +}; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor, softwareProductProcesses} = softwareProduct; + let {processToDelete} = softwareProductProcesses; + let softwareProductId = softwareProductEditor.data.id; + + const show = processToDelete !== false; + return { + show, + title: i18n('Warning!'), + type: 'warning', + msg: renderMsg(processToDelete), + confirmationDetails: {processToDelete, softwareProductId} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({processToDelete, softwareProductId}) => { + SoftwareProductProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId}); + SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js new file mode 100644 index 0000000000..63f3067a89 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + ADD_SOFTWARE_PRODUCT_PROCESS: null, + EDIT_SOFTWARE_PRODUCT_PROCESS: null, + DELETE_SOFTWARE_PRODUCT_PROCESS: null, + SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: null, + SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: null, + FETCH_SOFTWARE_PRODUCT_PROCESSES: null, + SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM: null, + processEditor: { + DATA_CHANGED: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js new file mode 100644 index 0000000000..8dc48c50b1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper'; +import SoftwareProductProcessesEditorView from './SoftwareProductProcessesEditorView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; + let {data} = processesEditor; + + let previousData; + const processId = data ? data.id : null; + if(processId) { + previousData = processesList.find(process => process.id === processId); + } + + return { + data, + previousData + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + return { + onDataChanged: deltaData => SoftwareProductProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onSubmit: ({previousProcess, process}) => { + SoftwareProductProcessesActionHelper.closeEditor(dispatch); + SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, process}); + }, + onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductProcessesEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js new file mode 100644 index 0000000000..cae25e2c89 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductProcessesConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: + return { + ...state, + data: action.process + }; + case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: + return {}; + + case actionTypes.processEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx new file mode 100644 index 0000000000..c2c4aff382 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import Dropzone from 'react-dropzone'; +import classnames from 'classnames'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + artifactName: React.PropTypes.string +}); + +class SoftwareProductProcessesEditorView extends React.Component { + + state = { + dragging: false, + files: [] + }; + + static propTypes = { + data: SoftwareProductProcessEditorPropType, + previousData: SoftwareProductProcessEditorPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func, + onSubmit: React.PropTypes.func, + onClose: React.PropTypes.func + }; + + render() { + let {data = {}, isReadOnlyMode, onDataChanged, onClose} = this.props; + let {name, description, artifactName} = data; + return ( + <ValidationForm + ref='validationForm' + hasButtons={true} + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + onSubmit={ () => this.submit() } + onReset={ () => onClose() } + className='vsp-processes-editor'> + <div className={classnames('vsp-processes-editor-data', {'disabled': isReadOnlyMode})}> + <Dropzone + className={classnames('vsp-process-dropzone-view', {'active-dragging': this.state.dragging})} + onDrop={files => this.handleImportSubmit(files)} + onDragEnter={() => this.setState({dragging: true})} + onDragLeave={() => this.setState({dragging: false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput' + accept='*.*'> + <div className='row'> + <div className='col-md-6'> + <ValidationInput + onChange={name => onDataChanged({name})} + label={i18n('Name')} + value={name} + validations={{validateName: true, maxLength: 120, required: true}} + type='text'/> + <ValidationInput + label={i18n('Artifacts')} + value={artifactName} + type='text' + disabled/> + </div> + <div className='col-md-6'> + <div className='file-upload-box'> + <div className='drag-text'>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <div className='upload-btn primary-btn' onClick={() => this.refs.processEditorFileInput.open()}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + </div> + </div> + <ValidationInput + onChange={description => onDataChanged({description})} + label={i18n('Notes')} + value={description} + name='vsp-process-description' + className='vsp-process-description' + validations={{maxLength: 1000}} + type='textarea'/> + </Dropzone> + </div> + </ValidationForm> + ); + } + + submit() { + const {data: process, previousData: previousProcess} = this.props; + let {files} = this.state; + let formData = false; + if (files.length) { + let file = files[0]; + formData = new FormData(); + formData.append('upload', file); + } + + let updatedProcess = { + ...process, + formData + }; + this.props.onSubmit({process: updatedProcess, previousProcess}); + } + + + handleImportSubmit(files) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } +} + +export default SoftwareProductProcessesEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js new file mode 100644 index 0000000000..619a2dba0f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductProcessesConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES: + return [...action.processesList]; + case actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS: + const indexForEdit = state.findIndex(process => process.id === action.process.id); + return [...state.slice(0, indexForEdit), action.process, ...state.slice(indexForEdit + 1)]; + case actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS: + return [...state, action.process]; + case actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS: + return state.filter(process => process.id !== action.processId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx new file mode 100644 index 0000000000..a2aa3d414e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx @@ -0,0 +1,112 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import SoftwareProductProcessesEditor from './SoftwareProductProcessesEditor.js'; +import SoftwareProductProcessesConfirmationModal from './SoftwareProductProcessesConfirmationModal.jsx'; + + +class SoftwareProductProcessesView extends React.Component { + + state = { + localFilter: '' + }; + + static propTypes = { + onAddProcess: React.PropTypes.func.isRequired, + onEditProcess: React.PropTypes.func.isRequired, + onDeleteProcess: React.PropTypes.func.isRequired, + isDisplayEditor: React.PropTypes.bool.isRequired, + isReadOnlyMode: React.PropTypes.bool.isRequired + }; + + render() { + let { currentSoftwareProduct} = this.props; + return ( + <div className='software-product-landing-view-right-side vsp-processes-page'> + {this.renderEditor()} + {this.renderProcessList()} + <SoftwareProductProcessesConfirmationModal softwareProductId={currentSoftwareProduct.id}/> + </div> + ); + } + + renderEditor() { + let {currentSoftwareProduct: {id}, isModalInEditMode, isReadOnlyMode, isDisplayEditor} = this.props; + return ( + + <Modal show={isDisplayEditor} bsSize='large' animation={true}> + <Modal.Header> + <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title> + </Modal.Header> + <Modal.Body className='edit-process-modal'> + <SoftwareProductProcessesEditor softwareProductId={id} isReadOnlyMode={isReadOnlyMode}/> + </Modal.Body> + </Modal> + ); + } + + renderProcessList() { + const {localFilter} = this.state; + let {onAddProcess, isReadOnlyMode} = this.props; + + return ( + <ListEditorView + plusButtonTitle={i18n('Add Process Details')} + filterValue={localFilter} + placeholder={i18n('Filter Process')} + onAdd={onAddProcess} + isReadOnlyMode={isReadOnlyMode} + onFilter={filter => this.setState({localFilter: filter})}> + {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} + </ListEditorView> + ); + } + + renderProcessListItem(process, isReadOnlyMode) { + let {id, name, description, artifactName = ''} = process; + let {onEditProcess, onDeleteProcess} = this.props; + return ( + <ListEditorItemView + key={id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditProcess(process)} + onDelete={() => onDeleteProcess(process)}> + + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Name')}</div> + <div className='name'>{name}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Artifact name')}</div> + <div className='artifact-name'>{artifactName}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Notes')}</div> + <div className='description'>{description}</div> + </div> + </ListEditorItemView> + ); + } + + filterList() { + let {processesList} = this.props; + let {localFilter} = this.state; + + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return processesList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return processesList; + } + } +} + +export default SoftwareProductProcessesView; |