diff options
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding')
98 files changed, 3415 insertions, 626 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js index 74bde4058b..2b59361eef 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -20,17 +20,23 @@ import LicenseKeyGroupsActionHelper from './licenseModel/licenseKeyGroups/Licens import EntitlementPoolsActionHelper from './licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; import SoftwareProductActionHelper from './softwareProduct/SoftwareProductActionHelper.js'; import SoftwareProductProcessesActionHelper from './softwareProduct/processes/SoftwareProductProcessesActionHelper.js'; +import SoftwareProductDeploymentActionHelper from './softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js'; import SoftwareProductNetworksActionHelper from './softwareProduct/networks/SoftwareProductNetworksActionHelper.js'; import SoftwareProductComponentsActionHelper from './softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductComponentProcessesActionHelper from './softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js'; import SoftwareProductComponentsNetworkActionHelper from './softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js'; import SoftwareProductDependenciesActionHelper from './softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js'; +import ComputeFlavorActionHelper from './softwareProduct/components/compute/ComputeFlavorActionHelper.js'; import OnboardActionHelper from './onboard/OnboardActionHelper.js'; import SoftwareProductComponentsMonitoringAction from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js'; import {actionTypes, enums} from './OnboardingConstants.js'; -import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -import ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; +import SoftwareProductComponentsImageActionHelper from './softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js'; +import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes, + onboardingMethod as onboardingMethodTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import ActivityLogActionHelper from 'sdc-app/common/activity-log/ActivityLogActionHelper.js'; +import licenseModelOverviewActionHelper from 'sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js'; import store from 'sdc-app/AppStore.js'; +import {selectedButton as licenseModelOverviewSelectedButton} from 'sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js'; function setCurrentScreen(dispatch, screen, props = {}) { dispatch({ @@ -92,7 +98,7 @@ export default { LicenseModelActionHelper.fetchLicenseModelItems(dispatch, {licenseModelId, version}).then(() =>{ setCurrentScreen(dispatch, enums.SCREEN.LICENSE_MODEL_OVERVIEW, {licenseModelId, version}); }); - + licenseModelOverviewActionHelper.selectVLMListView(dispatch, {buttonTab: licenseModelOverviewSelectedButton.VLM_LIST_VIEW}); }); }, navigateToLicenseAgreements(dispatch, {licenseModelId, version}) { @@ -158,9 +164,18 @@ export default { const newVersion = response[0].version ? response[0].version : version; SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}); - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion}); - SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); + let isFetchImageDetails = (response[0].onboardingMethod === onboardingMethodTypes.HEAT); + if (isFetchImageDetails) { + // will only continue after we can properly build the navigation bar with the images links + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion, isFetchImageDetails}).then(() => { + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); + }); + } else { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion, isFetchImageDetails}); + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); + } }); }, @@ -197,7 +212,11 @@ export default { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId, version}); }, - + navigateToSoftwareProductDeployment(dispatch, {softwareProductId, version}) { + SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + ComputeFlavorActionHelper.fetchComputesListForVSP(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT, {softwareProductId, version}); + }, navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version}){ ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: softwareProductId, versionId: version.id}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG, {softwareProductId, version}); @@ -224,6 +243,9 @@ export default { navigateToComponentCompute(dispatch, {softwareProductId, componentId, version}) { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + if (componentId && softwareProductId) { + ComputeFlavorActionHelper.fetchComputesList(dispatch, {softwareProductId, componentId, version}); + } setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, version, componentId}); }, @@ -253,6 +275,15 @@ export default { navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId, version}) { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, version, componentId}); + }, + + navigateToComponentImages(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsImageActionHelper.fetchImagesList(dispatch, { + softwareProductId, + componentId, + version + }); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES, {softwareProductId, version, componentId}); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js index 7811950073..0fff513cc5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js @@ -15,6 +15,8 @@ */ import keyMirror from 'nfvo-utils/KeyMirror.js'; +export const DATE_FORMAT = 'MM/DD/YYYY'; + export const actionTypes = keyMirror({ SET_CURRENT_SCREEN: null, SET_CURRENT_LICENSE_MODEL: null @@ -36,6 +38,7 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_DETAILS: 'SOFTWARE_PRODUCT_DETAILS', SOFTWARE_PRODUCT_ATTACHMENTS: 'SOFTWARE_PRODUCT_ATTACHMENTS', SOFTWARE_PRODUCT_PROCESSES: 'SOFTWARE_PRODUCT_PROCESSES', + SOFTWARE_PRODUCT_DEPLOYMENT: 'SOFTWARE_PRODUCT_DEPLOYMENT', SOFTWARE_PRODUCT_NETWORKS: 'SOFTWARE_PRODUCT_NETWORKS', SOFTWARE_PRODUCT_DEPENDENCIES: 'SOFTWARE_PRODUCT_DEPENDENCIES', SOFTWARE_PRODUCT_ACTIVITY_LOG: 'SOFTWARE_PRODUCT_ACTIVITY_LOG', @@ -45,7 +48,8 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_COMPONENT_GENERAL: 'SOFTWARE_PRODUCT_COMPONENT_GENERAL', SOFTWARE_PRODUCT_COMPONENT_COMPUTE: 'SOFTWARE_PRODUCT_COMPONENT_COMPUTE', SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: 'SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING', - SOFTWARE_PRODUCT_COMPONENT_MONITORING: 'SOFTWARE_PRODUCT_COMPONENT_MONITORING' + SOFTWARE_PRODUCT_COMPONENT_MONITORING: 'SOFTWARE_PRODUCT_COMPONENT_MONITORING', + SOFTWARE_PRODUCT_COMPONENT_IMAGES: 'SOFTWARE_PRODUCT_COMPONENT_IMAGES' }, SCREEN: { @@ -61,6 +65,7 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_DETAILS: null, SOFTWARE_PRODUCT_ATTACHMENTS: null, SOFTWARE_PRODUCT_PROCESSES: null, + SOFTWARE_PRODUCT_DEPLOYMENT: null, SOFTWARE_PRODUCT_NETWORKS: null, SOFTWARE_PRODUCT_DEPENDENCIES: null, SOFTWARE_PRODUCT_ACTIVITY_LOG: null, @@ -71,6 +76,7 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_COMPONENT_NETWORK: null, SOFTWARE_PRODUCT_COMPONENT_GENERAL: null, SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: null, - SOFTWARE_PRODUCT_COMPONENT_MONITORING: null + SOFTWARE_PRODUCT_COMPONENT_MONITORING: null, + SOFTWARE_PRODUCT_COMPONENT_IMAGES: null } }); diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx index e8a844b03f..1f0bef7a9f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -27,7 +27,7 @@ import Configuration from 'sdc-app/config/Configuration.js'; import Onboard from './onboard/Onboard.js'; import LicenseModel from './licenseModel/LicenseModel.js'; import LicenseModelOverview from './licenseModel/overview/LicenseModelOverview.js'; -import ActivityLog from 'nfvo-components/activity-log/ActivityLog.js'; +import ActivityLog from 'sdc-app/common/activity-log/ActivityLog.js'; import {doesHeatDataExist} from './softwareProduct/attachments/SoftwareProductAttachmentsUtils.js'; import LicenseAgreementListEditor from './licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; @@ -39,17 +39,25 @@ import SoftwareProductLandingPage from './softwareProduct/landingPage/SoftwareP import SoftwareProductDetails from './softwareProduct/details/SoftwareProductDetails.js'; import SoftwareProductAttachments from './softwareProduct/attachments/SoftwareProductAttachments.js'; import SoftwareProductProcesses from './softwareProduct/processes/SoftwareProductProcesses.js'; +import SoftwareProductDeployment from './softwareProduct/deployment/SoftwareProductDeployment.js'; import SoftwareProductNetworks from './softwareProduct/networks/SoftwareProductNetworks.js'; import SoftwareProductDependencies from './softwareProduct/dependencies/SoftwareProductDependencies.js'; -import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponentsList.js'; + +import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponents.js'; import SoftwareProductComponentProcessesList from './softwareProduct/components/processes/SoftwareProductComponentProcessesList.js'; import SoftwareProductComponentStorage from './softwareProduct/components/storage/SoftwareProductComponentStorage.js'; import SoftwareProductComponentsNetworkList from './softwareProduct/components/network/SoftwareProductComponentsNetworkList.js'; import SoftwareProductComponentsGeneral from './softwareProduct/components/general/SoftwareProductComponentsGeneral.js'; import SoftwareProductComponentsCompute from './softwareProduct/components/compute/SoftwareProductComponentCompute.js'; import SoftwareProductComponentLoadBalancing from './softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js'; +import SoftwareProductComponentsImageList from './softwareProduct/components/images/SoftwareProductComponentsImageList.js'; import SoftwareProductComponentsMonitoring from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js'; -import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import { + navigationItems as SoftwareProductNavigationItems, + onboardingMethod as onboardingMethodTypes, + actionTypes as SoftwareProductActionTypes +} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + import {statusEnum as VCItemStatus} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; @@ -123,6 +131,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: @@ -132,6 +141,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: return ( @@ -147,6 +157,8 @@ class OnboardingView extends React.Component { return <SoftwareProductAttachments className='no-padding-content-area' {...props} />; case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: return <SoftwareProductProcesses {...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: + return <SoftwareProductDeployment {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: return <SoftwareProductNetworks {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: @@ -165,6 +177,8 @@ class OnboardingView extends React.Component { return <SoftwareProductComponentsCompute {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: return <SoftwareProductComponentLoadBalancing{...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: + return <SoftwareProductComponentsImageList{...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: return <SoftwareProductComponentsMonitoring {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: @@ -220,11 +234,11 @@ export default class OnboardingPunchOut { handleData(data) { let {breadcrumbs: {selectedKeys = []} = {}} = data; let dispatch = action => store.dispatch(action); - let {currentScreen, softwareProductList, softwareProduct: {softwareProductEditor: {data: vspData = {}}, + let {currentScreen, softwareProductList, softwareProduct: {softwareProductEditor: {data: vspData = {}}, softwareProductComponents = {}, softwareProductQuestionnaire = {}}, licenseModelList, licenseModel: {licenseModelEditor: {data: {id: currentLicenseModelId, version: currentLicenseModelVersion} = {}}}} = store.getState(); let {id: currentSoftwareProductId, version: currentSoftwareProductVersion} = vspData; - let {componentEditor: {data: componentData = {}, qdata: componentQData = {}}} = softwareProductComponents; + let {componentEditor: {data: componentData = {}, qdata: componentQData = {}}} = softwareProductComponents; if (this.programmaticBreadcrumbsUpdate) { this.prevSelectedKeys = selectedKeys; this.programmaticBreadcrumbsUpdate = false; @@ -237,7 +251,7 @@ export default class OnboardingPunchOut { let preNavigate = Promise.resolve(); if(screenType === enums.BREADCRUMS.SOFTWARE_PRODUCT && vspData.status === VCItemStatus.CHECK_OUT_STATUS && VersionControllerUtils.isCheckedOutByCurrentUser(vspData)) { let dataToSave = prevVspId ? prevComponentId ? {componentData, qdata: componentQData} : {softwareProduct: vspData, qdata: softwareProductQuestionnaire.qdata} : {}; - preNavigate = OnboardingActionHelper.autoSaveBeforeNavigate(dispatch, { + preNavigate = OnboardingActionHelper.autoSaveBeforeNavigate(dispatch, { softwareProductId: prevVspId, version: currentSoftwareProductVersion, vspComponentId: prevComponentId, @@ -305,6 +319,9 @@ export default class OnboardingPunchOut { case enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES: OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT: + OnboardingActionHelper.navigateToSoftwareProductDeployment(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS: OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); break; @@ -355,22 +372,29 @@ export default class OnboardingPunchOut { OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: - OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: - OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK: - OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE: - OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: - OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING: - OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES: + OnboardingActionHelper.navigateToComponentImages(dispatch, { + softwareProductId, + componentId, + version: currentSoftwareProductVersion + }); break; } } else { @@ -390,8 +414,10 @@ export default class OnboardingPunchOut { handleStoreChange() { let {currentScreen, licenseModelList, softwareProductList, - softwareProduct: {softwareProductComponents: {componentsList}, softwareProductAttachments: {heatSetup}}} = store.getState(); - let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup}; + softwareProduct: {softwareProductEditor: {data = {onboardingMethod: ''}}, + softwareProductComponents: {componentsList, images: {imagesNavigationList}}, softwareProductAttachments: {heatSetup}}} = store.getState(); + let {onboardingMethod} = data; + let breadcrumbsData = {onboardingMethod, currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup, imagesNavigationList}; if (currentScreen.forceBreadCrumbsUpdate || !isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { this.prevBreadcrumbsData = breadcrumbsData; this.breadcrumbsPrefixSelected = false; @@ -408,7 +434,7 @@ export default class OnboardingPunchOut { } } - buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList, componentsList, heatSetup}) { + buildBreadcrumbs({currentScreen: {screen, props}, onboardingMethod, licenseModelList, softwareProductList, componentsList, heatSetup, imagesNavigationList}) { let screenToBreadcrumb; switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: @@ -474,6 +500,7 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: @@ -485,12 +512,14 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: screenToBreadcrumb = { [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_LANDING_PAGE, [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, + [enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT, [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS, [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, @@ -503,6 +532,7 @@ export default class OnboardingPunchOut { [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING }; let licenseModelId = softwareProductList.find(({id}) => id === props.softwareProductId).vendorId; @@ -542,6 +572,9 @@ export default class OnboardingPunchOut { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, displayText: i18n('General') }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT, + displayText: i18n('Deployment Flavors') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, displayText: i18n('Process Details') }, { @@ -561,14 +594,16 @@ export default class OnboardingPunchOut { displayText: i18n('Components') }].filter(item => { let isHeatData = doesHeatDataExist(heatSetup); - let isComponentsData = componentsList.length > 0; + let isManualMode = onboardingMethod === onboardingMethodTypes.MANUAL; switch (item.key) { case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: return isHeatData; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: - return isComponentsData; + return (componentsList.length > 0); + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT: + return isManualMode; case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES: - return isComponentsData; + return (componentsList.length > 1); default: return true; } @@ -603,12 +638,23 @@ export default class OnboardingPunchOut { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE, displayText: i18n('Storage') }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES, + displayText: i18n('Images') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, displayText: i18n('Process Details') }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING, displayText: i18n('Monitoring') - }] + }].filter(item => { + switch (item.key) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES: + return (onboardingMethod === onboardingMethodTypes.MANUAL || + (imagesNavigationList && imagesNavigationList[props.componentId] === true)); + default: + return true; + } + }) }] ]; } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js index e21b0a81b0..895a329047 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js @@ -18,7 +18,7 @@ import {connect} from 'react-redux'; import i18n from 'nfvo-utils/i18n/i18n.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; -import ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; +import ActivityLogActionHelper from 'sdc-app/common/activity-log/ActivityLogActionHelper.js'; import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js index 9a2d114bdc..bd060a4c28 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js @@ -15,7 +15,7 @@ */ import {combineReducers} from 'redux'; -import activityLogReducer from 'nfvo-components/activity-log/ActivityLogReducer.js'; +import activityLogReducer from 'sdc-app/common/activity-log/ActivityLogReducer.js'; import licenseModelCreationReducer from './creation/LicenseModelCreationReducer.js'; import licenseModelEditorReducer from './LicenseModelEditorReducer.js'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js index fe95b034dd..a7c95f608d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js @@ -39,7 +39,9 @@ function postEntitlementPool(licenseModelId, entitlementPool, version) { aggregationFunction: entitlementPool.aggregationFunction, operationalScope: entitlementPool.operationalScope, time: entitlementPool.time, - manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber + manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber, + startDate: entitlementPool.startDate, + expiryDate: entitlementPool.expiryDate }); } @@ -55,7 +57,9 @@ function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlement aggregationFunction: entitlementPool.aggregationFunction, operationalScope: entitlementPool.operationalScope, time: entitlementPool.time, - manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber + manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber, + startDate: entitlementPool.startDate, + expiryDate: entitlementPool.expiryDate }); } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js index ba0b238b17..761614dfeb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js @@ -113,3 +113,5 @@ export const extractUnits = (units) => { }; export const SP_ENTITLEMENT_POOL_FORM = 'SPENTITLEMENTPOOL'; + +export const EP_TIME_FORMAT = 'MM/DD/YYYY'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js index db1a3a97ca..bc9549765f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js @@ -14,10 +14,20 @@ * permissions and limitations under the License. */ import {actionTypes, defaultState, SP_ENTITLEMENT_POOL_FORM} from './EntitlementPoolsConstants.js'; +import moment from 'moment'; +import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.entitlementPoolsEditor.OPEN: + let entitlementPoolData = {...action.entitlementPool}; + let {startDate, expiryDate} = entitlementPoolData; + if (startDate) { + entitlementPoolData.startDate = moment(startDate, DATE_FORMAT).format(DATE_FORMAT); + } + if (expiryDate) { + entitlementPoolData.expiryDate = moment(expiryDate, DATE_FORMAT).format(DATE_FORMAT); + } return { ...state, formReady: null, @@ -72,9 +82,19 @@ export default (state = {}, action) => { isValid: true, errorText: '', validations: [{type: 'required', data: true}] + }, + 'startDate': { + isValid: true, + errorText: '', + validations: [] + }, + 'expiryDate': { + isValid: true, + errorText: '', + validations: [] } }, - data: action.entitlementPool ? {...action.entitlementPool} : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA + data: action.entitlementPool ? entitlementPoolData : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA }; case actionTypes.entitlementPoolsEditor.DATA_CHANGED: return { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx index d484437015..e4b52fc439 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx @@ -23,7 +23,7 @@ import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; import Form from 'nfvo-components/input/validation/Form.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType, SP_ENTITLEMENT_POOL_FORM} from './EntitlementPoolsConstants.js'; +import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType, SP_ENTITLEMENT_POOL_FORM, EP_TIME_FORMAT} from './EntitlementPoolsConstants.js'; import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; const EntitlementPoolPropType = React.PropTypes.shape({ @@ -50,10 +50,11 @@ const EntitlementPoolPropType = React.PropTypes.shape({ }) }); -const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, thresholdValueValidation}) => { +const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, + thresholdValueValidation, validateStartDate}) => { let { name, description, manufacturerReferenceNumber, operationalScope , aggregationFunction, thresholdUnits, thresholdValue, - increments, time, entitlementMetric} = data; + increments, time, entitlementMetric, startDate, expiryDate} = data; return ( <GridSection> @@ -175,6 +176,8 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber}, SP_ENTITLEMENT_POOL_FORM)} label={i18n('Manufacturer Reference Number')} value={manufacturerReferenceNumber} + isValid={genericFieldInfo.manufacturerReferenceNumber.isValid} + errorText={genericFieldInfo.manufacturerReferenceNumber.errorText} isRequired={true} data-test-id='create-ep-reference-number' type='text'/> @@ -206,6 +209,40 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val data-test-id='create-ep-increments' type='text'/> </GridItem> + <GridItem colSpan={2} /> + <GridItem colSpan={2}> + <Input + type='date' + label={i18n('Start Date')} + value={startDate} + dateFormat={EP_TIME_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={startDate => onDataChanged( + {startDate: startDate ? startDate.format(EP_TIME_FORMAT) : ''}, + SP_ENTITLEMENT_POOL_FORM, + {startDate: validateStartDate} + )} + isValid={genericFieldInfo.startDate.isValid} + errorText={genericFieldInfo.startDate.errorText} + selectsStart/> + </GridItem> + <GridItem colSpan={2}> + <Input + type='date' + label={i18n('Expiry Date')} + value={expiryDate} + dateFormat={EP_TIME_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={expiryDate => { + onDataChanged({expiryDate: expiryDate ? expiryDate.format(EP_TIME_FORMAT) : ''}, SP_ENTITLEMENT_POOL_FORM); + onDataChanged({startDate}, SP_ENTITLEMENT_POOL_FORM, {startDate: validateStartDate}); + }} + isValid={genericFieldInfo.expiryDate.isValid} + errorText={genericFieldInfo.expiryDate.errorText} + selectsEnd/> + </GridItem> </GridSection> ); }; @@ -251,6 +288,7 @@ class EntitlementPoolsEditorView extends React.Component { validateName={(value)=> this.validateName(value)} validateTimeOtherValue ={(value)=> this.validateTimeOtherValue(value)} validateChoiceWithOther={(value)=> this.validateChoiceWithOther(value)} + validateStartDate={(value, state)=> this.validateStartDate(value, state)} thresholdValueValidation={(value, state)=> this.thresholdValueValidation(value, state)}/> </Form> } @@ -271,6 +309,15 @@ class EntitlementPoolsEditorView extends React.Component { {isValid: false, errorText: i18n('Entitlement pool by the name \'' + value + '\' already exists. Entitlement pool name must be unique')}; } + validateStartDate(value, state) { + if (state.data.expiryDate) { + if (!value) { + return {isValid: false, errorText: i18n('Start date has to be specified if expiry date is specified')}; + } + } + return {isValid: true, errorText: ''}; + } + validateTimeOtherValue(value) { return Validator.validate('time', value.other, [{type: 'required', data: true}, {type: 'numeric', data: true}]); } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx index 07a6f21a1a..55fd11b8bb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx @@ -45,14 +45,14 @@ class EntitlementPoolsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; + let {licenseModelId, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; let {onAddEntitlementPoolClick} = this.props; const {localFilter} = this.state; return ( <div className='entitlement-pools-list-editor'> <ListEditorView - title={i18n('Entitlement Pools', {vendorName})} + title={i18n('Entitlement Pools')} plusButtonTitle={i18n('Add Entitlement Pool')} onAdd={onAddEntitlementPoolClick} filterValue={localFilter} @@ -132,7 +132,7 @@ export default EntitlementPoolsListEditorView; export function generateConfirmationMsg(entitlementPoolToDelete) { let poolName = entitlementPoolToDelete ? entitlementPoolToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{poolName}"?', {poolName}); + let msg = i18n(`Are you sure you want to delete "${poolName}"?`); let subMsg = entitlementPoolToDelete && entitlementPoolToDelete.referencingFeatureGroups && entitlementPoolToDelete.referencingFeatureGroups.length > 0 ? diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx index bc0f5c71c0..f883bd7a14 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -51,12 +51,12 @@ class FeatureGroupListEditorView extends React.Component { }; render() { - let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick, version} = this.props; + let {licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick, version} = this.props; const {localFilter} = this.state; return ( <div className='feature-groups-list-editor'> <ListEditorView - title={i18n('Feature Groups', {vendorName})} + title={i18n('Feature Groups')} plusButtonTitle={i18n('Add Feature Group')} filterValue={localFilter} onFilter={value => this.setState({localFilter: value})} @@ -146,7 +146,7 @@ export default FeatureGroupListEditorView; export function generateConfirmationMsg(featureGroupToDelete) { let name = featureGroupToDelete ? featureGroupToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let msg = i18n(`Are you sure you want to delete "${name}"?`); let subMsg = featureGroupToDelete.referencingLicenseAgreements && featureGroupToDelete.referencingLicenseAgreements.length > 0 ? i18n('This feature group is associated with one ore more license agreements') : diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js index 373694f2bf..72a99e26ce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js @@ -44,7 +44,7 @@ const mapActionsToProps = (dispatch, {licenseModelId}) => { onDeleteLicenseAgreement: (licenseAgreement, version) => dispatch({ type: globalMoadlActions.GLOBAL_MODAL_WARNING, data:{ - msg: i18n('Are you sure you want to delete "{name}"?', {name: licenseAgreement.name}), + msg: i18n(`Are you sure you want to delete "${licenseAgreement.name}"?`), title: i18n('Warning'), onConfirmed: ()=>LicenseAgreementActionHelper.deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId: licenseAgreement.id, version}) } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx index 776b04b8eb..192d2ded99 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx @@ -44,14 +44,14 @@ class LicenseAgreementListEditorView extends React.Component { }; render() { - const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; + const {licenseModelId, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; const {onAddLicenseAgreementClick} = this.props; const {localFilter} = this.state; return ( <div className='license-agreement-list-editor'> <ListEditorView - title={i18n('License Agreements', {vendorName})} + title={i18n('License Agreements')} plusButtonTitle={i18n('Add License Agreement')} onAdd={() => onAddLicenseAgreementClick(version)} filterValue={localFilter} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx index a303e46706..b8ccd68bce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx @@ -46,14 +46,14 @@ class LicenseKeyGroupsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; + let {licenseModelId, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; let {onAddLicenseKeyGroupClick} = this.props; const {localFilter} = this.state; return ( <div className='license-key-groups-list-editor'> <ListEditorView - title={i18n('License Key Groups', {vendorName})} + title={i18n('License Key Groups')} plusButtonTitle={i18n('Add License Key Group')} onAdd={onAddLicenseKeyGroupClick} filterValue={localFilter} @@ -147,7 +147,7 @@ export default LicenseKeyGroupsListEditorView; export function generateConfirmationMsg(licenseKeyGroupToDelete) { let name = licenseKeyGroupToDelete ? licenseKeyGroupToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let msg = i18n(`Are you sure you want to delete "${name}"?`); let subMsg = licenseKeyGroupToDelete.referencingFeatureGroups && licenseKeyGroupToDelete.referencingFeatureGroups.length > 0 ? i18n('This license key group is associated with one or more feature groups') : diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx index c63fbff21b..3b3e2fcf40 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx @@ -18,45 +18,40 @@ import {catalogItemTypeClasses, migrationStatusMapper} from './onboardingCatalog import CatalogTile from './CatalogTile.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import {statusEnum, statusBarTextMap} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import tooltip from './onboardingCatalog/Tooltip.jsx'; - const CatalogTileIcon = ({catalogItemTypeClass}) => ( - <div className={'catalog-tile-icon ' + catalogItemTypeClass}> - <div className='icon'><SVGIcon - name={catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? 'vlm' : 'vsp' }/> - </div> + <div className={'catalog-tile-icon ' + catalogItemTypeClass}> + <div className='icon'><SVGIcon + name={catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? 'vlm' : 'vsp' }/> </div> + </div> ); const ItemTypeTitle = ({catalogItemTypeClass}) => { const itemTypeTitle = catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? i18n('VLM') : i18n('VSP'); - return( + return ( <div className={`catalog-tile-type ${catalogItemTypeClass}`}>{itemTypeTitle}</div> ); }; -const CatalogTileVendorName = ({vendorName, catalogItemTypeClass}) => { - const name = catalogItemTypeClass === catalogItemTypeClasses.SOFTWARE_PRODUCT ? vendorName : ''; - return( - <div> - <OverlayTrigger placement='top' overlay={tooltip(name)}> - <div className='catalog-tile-vendor-name'>{name}</div> - </OverlayTrigger> - </div> +const CatalogTileVendorName = ({vendorName, catalogItemTypeClass}) => { + const name = catalogItemTypeClass === catalogItemTypeClasses.SOFTWARE_PRODUCT ? vendorName : ''; + return ( name ? + <OverlayTrigger placement='top' overlay={tooltip(name)}> + <div className='catalog-tile-vendor-name'>{name}</div> + </OverlayTrigger> : <div className='catalog-tile-vendor-name'>{name}</div> ); }; const CatalogTileItemName = ({name}) => ( - <div> - <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> - <div className='catalog-tile-item-name'>{name}</div> - </OverlayTrigger> - </div> + <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> + <div className='catalog-tile-item-name'>{name}</div> + </OverlayTrigger> ); const VersionInfo = ({version}) => ( @@ -64,7 +59,7 @@ const VersionInfo = ({version}) => ( <div className='catalog-tile-item-version' data-test-id='catalog-item-version'> V {version} </div> - </div> + </div> ); const EntityDetails = ({catalogItemData, catalogItemTypeClass}) => { @@ -73,54 +68,55 @@ const EntityDetails = ({catalogItemData, catalogItemTypeClass}) => { <div className='catalog-tile-entity-details'> <CatalogTileVendorName catalogItemTypeClass={catalogItemTypeClass} vendorName={vendorName}/> <CatalogTileItemName name={name}/> - <VersionInfo version={version.label} /> - </div> + <VersionInfo version={version.label}/> + </div> ); }; -const ItemStatusInfo = ({catalogItemTypeClass, lockingUser, itemStatus}) => { +const ItemStatusInfo = ({catalogItemTypeClass, lockingUser, itemStatus}) => { const status = statusBarTextMap[itemStatus]; const lockedBy = lockingUser ? ` by ${lockingUser}` : ''; const toolTipMsg = `${status}${lockedBy}`; return ( - <div className={'catalog-tile-content ' + catalogItemTypeClass}> + <div className={'catalog-tile-content ' + catalogItemTypeClass}> <div className='catalog-tile-locking-user-name'>{i18n(status)}</div> - <OverlayTrigger placement='top' overlay={tooltip(toolTipMsg)}> + <OverlayTrigger placement='top' overlay={tooltip(toolTipMsg)}> <div className='catalog-tile-check-in-status'><SVGIcon name={itemStatus === statusEnum.CHECK_OUT_STATUS ? 'unlocked' : 'locked'} data-test-id={itemStatus === statusEnum.CHECK_IN_STATUS ? 'catalog-item-checked-in' : 'catalog-item-checked-out'}/> </div> - </OverlayTrigger> + </OverlayTrigger> </div> - + ); }; -const CatalogItemDetails = ({catalogItemData, catalogItemTypeClass, onSelect, onMigrate}) => { - +const CatalogItemDetails = ({catalogItemData, catalogItemTypeClass, onSelect, onMigrate}) => { + let {status: itemStatus} = VersionControllerUtils.getCheckOutStatusKindByUserID(catalogItemData.status, catalogItemData.lockingUser); - + return ( <CatalogTile catalogItemTypeClass={catalogItemTypeClass} onSelect={() => { if (catalogItemData.isOldVersion && catalogItemData.isOldVersion === migrationStatusMapper.OLD_VERSION) { onMigrate({ softwareProduct: catalogItemData }); - } else { + } + else { onSelect(); } }} data-test-id={catalogItemTypeClass}> - <div className='catalog-tile-top item-details'> - <ItemTypeTitle catalogItemTypeClass={catalogItemTypeClass}/> - <CatalogTileIcon catalogItemTypeClass={catalogItemTypeClass}/> - <EntityDetails catalogItemTypeClass={catalogItemTypeClass} catalogItemData={catalogItemData} /> - <ItemStatusInfo itemStatus={itemStatus} catalogItemTypeClass={catalogItemTypeClass} lockingUser={catalogItemData.lockingUser} /> + <div className='catalog-tile-top item-details'> + <ItemTypeTitle catalogItemTypeClass={catalogItemTypeClass}/> + <CatalogTileIcon catalogItemTypeClass={catalogItemTypeClass}/> + <EntityDetails catalogItemTypeClass={catalogItemTypeClass} catalogItemData={catalogItemData}/> + <ItemStatusInfo itemStatus={itemStatus} catalogItemTypeClass={catalogItemTypeClass} lockingUser={catalogItemData.lockingUser}/> </div> </CatalogTile> ); - + }; CatalogItemDetails.PropTypes = { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx index 17248e3b02..51702e6d36 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx @@ -15,7 +15,7 @@ */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; const SoftwareProductListHeader = ({selectedVendor, onBack}) => ( <div className='vendor-page-header'> diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js index 73a447558d..0d1e3992ce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js @@ -26,7 +26,7 @@ import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/Soft function getMessageForMigration(name) { return ( <div> - <div>{i18n('{name} needs to be updated. Click ‘Checkout & Update’, to proceed.',{name})}</div> + <div>{i18n(`${name} needs to be updated. Click ‘Checkout & Update’, to proceed.`)}</div> <div>{i18n('Please don’t forget to submit afterwards')}</div> </div> ); @@ -65,7 +65,7 @@ const OnboardingCatalogActionHelper = { type: modalActionTypes.GLOBAL_MODAL_WARNING, data: { title: 'WARNING', - msg: i18n('{name} is locked by user {lockingUser} for self-healing',{name, lockingUser}) + msg: i18n(`${name} is locked by user ${lockingUser} for self-healing`) } }); } else { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx index cecccdd9ad..d3d6f9ce37 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx @@ -19,7 +19,7 @@ import CatalogTile from '../CatalogTile.jsx'; import classnames from 'classnames'; import VSPOverlay from './VSPOverlay.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import tooltip from './Tooltip.jsx'; @@ -50,7 +50,7 @@ class VendorItem extends React.Component { </div> <OverlayTrigger placement='top' overlay={tooltip(vendorName)}> <div className='catalog-tile-item-name'>{vendorName}</div> - </OverlayTrigger> + </OverlayTrigger> <div className={classnames('catalog-tile-vsp-count', {active: shouldShowOverlay}, {clickable: softwareProductList.length})} onClick={(event) => this.handleVspCountClick(event)} @@ -63,7 +63,7 @@ class VendorItem extends React.Component { </div> </div> </div> - + {shouldShowOverlay && softwareProductList.length > 0 && <VSPOverlay onMigrate={onMigrate} VSPList={softwareProductList} onSelectVSP={onSelectVSP} onSeeMore={() => onVendorSelect(vendor)}/>} </CatalogTile> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js index 12f68a2afe..07d6c740e0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -22,7 +22,7 @@ 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, mapScreenToNavigationItem} from './SoftwareProductConstants.js'; +import {navigationItems, mapScreenToNavigationItem, onboardingMethod as onboardingMethodTypes} from './SoftwareProductConstants.js'; import SoftwareProductActionHelper from './SoftwareProductActionHelper.js'; import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductDependenciesActionHelper from './dependencies/SoftwareProductDependenciesActionHelper.js'; @@ -36,7 +36,7 @@ function getActiveNavigationId(screen, componentId) { return activeItemId; } -const buildComponentNavigationBarGroups = ({componentId, meta}) => { +const buildComponentNavigationBarGroups = ({componentId, meta, hasImages}) => { const groups = ([ { id: navigationItems.GENERAL + '|' + componentId, @@ -64,6 +64,12 @@ const buildComponentNavigationBarGroups = ({componentId, meta}) => { disabled: false, meta }, { + id: navigationItems.IMAGES + '|' + componentId, + name: i18n('Images'), + disabled: false, + hidden: (!hasImages), + meta + }, { id: navigationItems.PROCESS_DETAILS + '|' + componentId, name: i18n('Process Details'), disabled: false, @@ -79,9 +85,9 @@ const buildComponentNavigationBarGroups = ({componentId, meta}) => { return groups; }; -const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) => { +const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds, imagesNavigationList}) => { const {softwareProductEditor: {data: currentSoftwareProduct = {}}} = softwareProduct; - const {id, name} = currentSoftwareProduct; + const {id, name, onboardingMethod} = currentSoftwareProduct; const groups = [{ id: id, name: name, @@ -96,6 +102,13 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co name: i18n('General'), disabled: false, meta + }, + { + id: navigationItems.DEPLOYMENT_FLAVORS, + name: i18n('Deployment Flavors'), + disabled: false, + hidden: onboardingMethod !== onboardingMethodTypes.MANUAL, + meta }, { id: navigationItems.PROCESS_DETAILS, name: i18n('Process Details'), @@ -135,7 +148,8 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co name: displayName, meta, expanded: mapOfExpandedIds[navigationItems.COMPONENTS + '|' + id] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, - items: buildComponentNavigationBarGroups({componentId: id, meta}) + items: buildComponentNavigationBarGroups({componentId: id, meta, + hasImages : (onboardingMethod === onboardingMethodTypes.MANUAL || imagesNavigationList[id] === true)}) })) ] } @@ -179,17 +193,18 @@ function buildMeta({softwareProduct, componentId, softwareProductDependencies}) const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { const {softwareProductEditor, softwareProductComponents, softwareProductDependencies} = softwareProduct; const {mapOfExpandedIds = []} = softwareProductEditor; - const {componentsList = []} = softwareProductComponents; + const {componentsList = [], images: {imagesNavigationList}} = softwareProductComponents; + const meta = buildMeta({softwareProduct, componentId, softwareProductDependencies}); return { versionControllerProps: buildVersionControllerProps(softwareProduct), - navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}), + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds, imagesNavigationList}), meta }; }; -const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, - meta: {isReadOnlyMode, softwareProduct, version, qdata, softwareProductDependencies, +const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, + meta: {isReadOnlyMode, softwareProduct, version, qdata, softwareProductDependencies, currentComponentMeta: {componentData, componentQdata}}}) => { let promise; if (isReadOnlyMode) { @@ -208,6 +223,7 @@ const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentI 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_IMAGES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata: componentQdata}); break; @@ -242,6 +258,9 @@ const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentC case navigationItems.NETWORKS: OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; + case navigationItems.IMAGES: + OnboardingActionHelper.navigateToComponentImages(dispatch, {softwareProductId, componentId: nextComponentId, version}); + break; case navigationItems.STORAGE: OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; @@ -266,7 +285,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr let {heatSetup, heatSetupCache} = meta; let heatSetupPopupPromise = screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS ? HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : - Promise.resolve(); + Promise.resolve(); let preNavigate = meta ? autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}) : Promise.resolve(); version = version || (meta ? meta.version : undefined); Promise.all([preNavigate, heatSetupPopupPromise]).then(() => { @@ -277,6 +296,9 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr case navigationItems.GENERAL: OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId, version}); break; + case navigationItems.DEPLOYMENT_FLAVORS: + OnboardingActionHelper.navigateToSoftwareProductDeployment(dispatch, {softwareProductId, version}); + break; case navigationItems.PROCESS_DETAILS: OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}); break; @@ -299,7 +321,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr onComponentNavigate(dispatch, {id, softwareProductId, version, screen, currentComponentId}); break; } - }).catch(() => {}); + }).catch((e) => {console.error(e);}); } }; @@ -311,6 +333,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: props.onSave = () => { @@ -335,7 +358,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version: newVersion}); } }); - }).catch(() => {}); + }).catch((e) => {console.error(e);}); }; return props; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js index 6f53886350..d6ba86ad6e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -27,6 +27,7 @@ import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel import {actionTypes as HeatSetupActions} from 'sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js'; import {actionTypes as featureGroupsActionConstants} from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js'; import {actionTypes as licenseAgreementActionTypes} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js'; +import {actionTypes as componentActionTypes} from './components/SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; @@ -57,7 +58,8 @@ function putSoftwareProduct(softwareProduct) { vendorName: softwareProduct.vendorName, licensingVersion: softwareProduct.licensingVersion && softwareProduct.licensingVersion.id ? softwareProduct.licensingVersion : {} , icon: softwareProduct.icon, - licensingData: softwareProduct.licensingData + licensingData: softwareProduct.licensingData, + onboardingMethod: softwareProduct.onboardingMethod }); } @@ -249,7 +251,8 @@ const SoftwareProductActionHelper = { processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){ return validateHeatCandidate(softwareProductId, version).then(response => { if (response.status === 'Success') { - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + let isFetchImageDetails = true; + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails}); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); } }); @@ -459,8 +462,20 @@ const SoftwareProductActionHelper = { }, /** for the next verision */ - addComponent(dispatch) { - return dispatch; + addComponent(dispatch, {softwareProductId, modalClassName}) { + SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch); + dispatch({ + type: componentActionTypes.COMPONENT_CREATE_OPEN + }); + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.COMPONENT_CREATION, + modalComponentProps: {softwareProductId}, + modalClassName, + title: 'Create Virtual Function Component' + } + }); }, migrateSoftwareProduct(dispatch, {softwareProduct}) { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js index f29b0f6e0d..0379ee5d4a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js @@ -38,11 +38,13 @@ export const navigationItems = keyMirror({ VENDOR_SOFTWARE_PRODUCT: 'vendor-software-product', GENERAL: 'general', PROCESS_DETAILS: 'process-details', - NETWORKS: 'networks', - DEPENDENCIES: 'dependencies', + DEPLOYMENT_FLAVORS: 'deployment-flavor', + NETWORKS: 'networks', + IMAGES: 'images', ATTACHMENTS: 'attachments', ACTIVITY_LOG: 'activity-log', COMPONENTS: 'components', + DEPENDENCIES: 'dependencies', COMPUTE: 'compute', LOAD_BALANCING: 'load-balancing', @@ -50,6 +52,11 @@ export const navigationItems = keyMirror({ MONITORING: 'monitoring' }); +export const onboardingMethod = { + MANUAL: 'Manual', + HEAT: 'HEAT' +}; + export const forms = keyMirror({ VENDOR_SOFTWARE_PRODUCT_DETAILS: 'vendor-software-product-details', }); @@ -61,6 +68,7 @@ export const mapScreenToNavigationItem = { [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_DEPLOYMENT]: navigationItems.DEPLOYMENT_FLAVORS, [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS, [enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG]: navigationItems.ACTIVITY_LOG, [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: navigationItems.DEPENDENCIES, @@ -69,6 +77,7 @@ export const mapScreenToNavigationItem = { [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_IMAGES]: navigationItems.IMAGES, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js index 97988d87f9..5248c4e8fd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -22,6 +22,8 @@ import SoftwareProductCreationReducer from './creation/SoftwareProductCreationRe import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js'; import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js'; import SoftwareProductProcessesEditorReducer from './processes/SoftwareProductProcessesEditorReducer.js'; +import SoftwareProductDeploymentListReducer from './deployment/SoftwareProductDeploymentListReducer.js'; +import SoftwareProductDeploymentEditorReducer from './deployment/editor/SoftwareProductDeploymentEditorReducer.js'; import SoftwareProductNetworksListReducer from './networks/SoftwareProductNetworksListReducer.js'; import SoftwareProductComponentsListReducer from './components/SoftwareProductComponentsListReducer.js'; import SoftwareProductComponentEditorReducer from './components/SoftwareProductComponentEditorReducer.js'; @@ -31,12 +33,19 @@ import SoftwareProductComponentProcessesEditorReducer from './components/process import {actionTypes as componentProcessesActionTypes} from './components/processes/SoftwareProductComponentProcessesConstants.js'; import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js'; import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; +import SoftwareProductComponentsImageListReducer from './components/images/SoftwareProductComponentsImageListReducer.js'; +import SoftwareProductComponentsImageEditorReducer from './components/images/SoftwareProductComponentsImageEditorReducer.js'; +import SoftwareProductComponentsImageNavigationReducer from './components/images/SoftwareProductComponentsImageNavigationReducer.js'; +import SoftwareProductComponentsNICCreationReducer from './components/network/NICCreation/NICCreationReducer.js'; import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; +import SoftwareProductComponentsComputeFlavorListReducer from './components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js'; +import SoftwareProductComponentsComputeFlavorReducer from './components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js'; import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; import SoftwareProductDependenciesReducer from './dependencies/SoftwareProductDependenciesReducer.js'; import {createJSONSchemaReducer, createComposedJSONSchemaReducer} from 'sdc-app/common/reducers/JSONSchemaReducer.js'; -import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {COMPONENTS_QUESTIONNAIRE, COMPONENTS_COMPUTE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; +import {IMAGE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js'; export default combineReducers({ softwareProductAttachments: combineReducers({ @@ -51,6 +60,10 @@ export default combineReducers({ processesEditor: createPlainDataReducer(SoftwareProductProcessesEditorReducer), processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state }), + softwareProductDeployment: combineReducers({ + deploymentFlavors: SoftwareProductDeploymentListReducer, + deploymentFlavorEditor: createPlainDataReducer(SoftwareProductDeploymentEditorReducer) + }), softwareProductNetworks: combineReducers({ networksList: SoftwareProductNetworksListReducer }), @@ -65,7 +78,17 @@ export default combineReducers({ }), network: combineReducers({ nicList: SoftwareProductComponentsNICListReducer, - nicEditor: createPlainDataReducer(createComposedJSONSchemaReducer(NIC_QUESTIONNAIRE, SoftwareProductComponentsNICEditorReducer)) + nicEditor: createPlainDataReducer(createComposedJSONSchemaReducer(NIC_QUESTIONNAIRE, SoftwareProductComponentsNICEditorReducer)), + nicCreation: createPlainDataReducer(SoftwareProductComponentsNICCreationReducer) + }), + images: combineReducers({ + imagesList: SoftwareProductComponentsImageListReducer, + imagesNavigationList: SoftwareProductComponentsImageNavigationReducer, + imageEditor: createPlainDataReducer(createComposedJSONSchemaReducer(IMAGE_QUESTIONNAIRE, SoftwareProductComponentsImageEditorReducer)) + }), + computeFlavor: combineReducers({ + computesList: SoftwareProductComponentsComputeFlavorListReducer, + computeEditor: createPlainDataReducer(createComposedJSONSchemaReducer(COMPONENTS_COMPUTE_QUESTIONNAIRE, SoftwareProductComponentsComputeFlavorReducer)), }), monitoring: SoftwareProductComponentsMonitoringReducer }), diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx index 0d8bc58361..901a583e24 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx @@ -14,14 +14,14 @@ * permissions and limitations under the License. */ import React, {Component} from 'react'; -import Button from 'react-bootstrap/lib/Button.js'; +import Button from 'sdc-ui/lib/react/Button.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import FormControl from 'react-bootstrap/lib/FormControl.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import SelectInput from 'nfvo-components/input/SelectInput.jsx'; import Icon from 'nfvo-components/icon/Icon.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import {fileTypes} from './HeatSetupConstants.js'; import {tabsMapping} from '../SoftwareProductAttachmentsConstants.js'; import {sortable} from 'react-sortable'; @@ -76,8 +76,8 @@ class SortableModuleFileList extends Component { <div className='modules-list-wrapper'> <div className='modules-list-header'> <div className='modules-list-controllers'> - {!isBaseExist && <Button bsStyle='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} - <Button bsStyle='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> + {!isBaseExist && <Button btnType='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} + <Button btnType='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> </div> </div> <ul>{listItems}</ul> @@ -102,7 +102,7 @@ const EmptyListContent = props => { return ( <div className='go-to-validation-button-wrapper'> <div className='all-files-assigned'>{i18n(displayText)}</div> - {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angle-right'/></div>} + {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angleRight'/></div>} </div> ); }; @@ -212,7 +212,7 @@ class ModuleFile extends Component { data-test-id={isBase ? 'base-name' : 'module-name'}/>} </div> </div> - <SVGIcon name='trash-o' onClick={() => onModuleDelete(name)} data-test-id='module-delete'/> + <SVGIcon name='trashO' onClick={() => onModuleDelete(name)} data-test-id='module-delete'/> </div> <div className='modules-list-item-selectors'> <SelectWithFileType diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx index 25ad90f351..f2d5de4dff 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx @@ -17,7 +17,7 @@ import React, {Component, PropTypes} from 'react'; import classNames from 'classnames'; import Collapse from 'react-bootstrap/lib/Collapse.js'; import Icon from 'nfvo-components/icon/Icon.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js'; @@ -68,7 +68,7 @@ function HeatFileTreeRow(props) { isFolder && <div onClick={() => toggleExpanded(path)} className='tree-node-expander'> - <SVGIcon name={!node.expanded ? 'chevron-up' : 'chevron-down'} data-test-id='validation-tree-block-toggle'/> + <SVGIcon name={!node.expanded ? 'chevronUp' : 'chevronDown'} data-test-id='validation-tree-block-toggle'/> </div> } { @@ -205,23 +205,19 @@ class HeatMessageBoard extends Component { key={error.name + error.errorMessage + error.parentName + rand} className='error-item' data-test-id='validation-error'> {error.level === errorLevels.WARNING ? - <SVGIcon name='exclamation-triangle-line' iconClassName='large' /> : <Icon image='error-lg' /> } + <SVGIcon name='exclamationTriangleLine' iconClassName='large' /> : <Icon image='error-lg' /> } <span className='error-item-file-type'> { (this.props.selectedNode === nodeFilters.ALL) ? <span> <span className='error-file-name'> - {i18n('{errorName}:', { - errorName: error.name - })} + {i18n(`${error.name}`)} </span> <span> - {i18n('{message}', {message: error.errorMessage})} + {i18n(error.errorMessage)} </span> </span> : - i18n('{errorMsg}', { - errorMsg: error.errorMessage - }) + i18n(error.errorMesage) } </span> </div> @@ -249,7 +245,7 @@ class ErrorsAndWarningsCount extends Component { <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>{errors.errorCount}</div> </div>} {(errors.warningCount > 0) && <div className='counter'> - <SVGIcon name='exclamation-triangle-line' iconClassName={size} /> + <SVGIcon name='exclamationTriangleLine' iconClassName={size} /> <div className={'warning-text ' + (size ? size : '')} data-test-id='validation-warning-count'>{errors.warningCount}</div> </div>} </div>); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js index 41e7556749..b13bde03c8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js @@ -17,6 +17,24 @@ import {actionTypes, forms} from './SoftwareProductComponentsConstants.js'; export default (state = {}, action) => { switch (action.type) { + case actionTypes.COMPONENT_CREATE_OPEN: + return { + ...state, + formName: forms.CREATE_FORM, + formReady: null, + genericFieldInfo: { + 'displayName' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'validateName', data: true}, {type: 'maxLength', data: 25}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + } + } + }; case actionTypes.COMPONENT_LOAD: return { ...state, @@ -34,6 +52,11 @@ export default (state = {}, action) => { errorText: '', validations: [] }, + 'nfcFunction' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 30}] + }, 'description' : { isValid: true, errorText: '', @@ -41,6 +64,27 @@ export default (state = {}, action) => { } } }; + 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 + } + }; + case actionTypes.COMPONENT_DATA_CLEAR: + return {}; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js new file mode 100644 index 0000000000..61aebdf293 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js @@ -0,0 +1,65 @@ +import {connect} from 'react-redux'; +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import SoftwareProductComponentsList from './SoftwareProductComponentsList.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductComponentsActionHelper from '../components/SoftwareProductComponentsActionHelper.js'; +import {onboardingMethod} from '../SoftwareProductConstants.js'; +import ConfirmationModalConstants from 'nfvo-components/modal/GlobalModalConstants.js'; + +const generateMessage = (name) => { + return i18n(`Are you sure you want to delete ${name}?`); +}; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductComponents} = softwareProduct; + let {componentsList} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isReadOnlyMode, + componentsList, + isManual: currentSoftwareProduct.onboardingMethod === onboardingMethod.MANUAL + + }; +}; + +class SoftwareProductComponentsView extends React.Component { + render() { + let {currentSoftwareProduct, isReadOnlyMode, componentsList, isManual, onDeleteComponent} = this.props; + return ( + <SoftwareProductComponentsList + isReadOnlyMode={isReadOnlyMode} + componentsList={componentsList} + onDeleteComponent={onDeleteComponent} + isManual={isManual} + currentSoftwareProduct={currentSoftwareProduct}/>); + } + +} + +const mapActionToProps = (dispatch) => { + return { + onComponentSelect: ({id: softwareProductId, componentId, version}) => { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); + }, + onAddComponent: (softwareProductId) => SoftwareProductComponentsActionHelper.addComponent(dispatch, {softwareProductId}), + onDeleteComponent: (component, softwareProductId, version) => dispatch({ + type: ConfirmationModalConstants.GLOBAL_MODAL_WARNING, + data:{ + msg: generateMessage(component.displayName), + onConfirmed: ()=>SoftwareProductComponentsActionHelper.deleteComponent(dispatch, + { + softwareProductId, + componentId: component.id, + version + }) + } + }) + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js index 4e526d3b56..71dc8325ad 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -18,6 +18,8 @@ import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes, COMPONENTS_QUESTIONNAIRE} from './SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductComponentsImageActionHelper from './images/SoftwareProductComponentsImageActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; function baseUrl(softwareProductId, version) { const versionId = version.id; @@ -46,17 +48,53 @@ function putSoftwareProductComponent(softwareProductId, version, vspComponentId, name: vspComponent.name, displayName: vspComponent.displayName, vfcCode: vspComponent.vfcCode, + nfcFunction: vspComponent.nfcFunction, description: vspComponent.description }); } +function deleteSoftwareProductComponent(softwareProductId, componentId, version) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version)}/${componentId}`,); +} + + +function postSoftwareProductComponent(softwareProductId, vspComponent, version) { + + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, { + name: vspComponent.displayName, + displayName: vspComponent.displayName, + description: vspComponent.description + }); +} + + const SoftwareProductComponentsActionHelper = { - fetchSoftwareProductComponents(dispatch, {softwareProductId, version}) { + fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails = false}) { return fetchSoftwareProductComponents(softwareProductId, version).then(response => { - dispatch({ - type: actionTypes.COMPONENTS_LIST_UPDATE, - componentsList: response.results - }); + let componentImagesCalls = []; + if (isFetchImageDetails && response.listCount) { + response.results.map(component => { + let componentId = component.id; + componentImagesCalls[componentImagesCalls.length] = + SoftwareProductComponentsImageActionHelper.fetchImagesList(dispatch, { + softwareProductId, + componentId, + version + }); + + }); + return Promise.all(componentImagesCalls).then(() => { + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); + }); + } else { + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); + } }); }, @@ -110,7 +148,45 @@ const SoftwareProductComponentsActionHelper = { type: actionTypes.COMPONENTS_LIST_UPDATE, componentsList: [] }); - } + }, + + createSoftwareProductComponent(dispatch,{softwareProductId, componentData, version}) { + SoftwareProductComponentsActionHelper.closeComponentCreationModal(dispatch); + /* for mock only */ + + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: [{id: '123', ...componentData}] + }); + + postSoftwareProductComponent(softwareProductId, componentData, version).then(() => { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + }); + }, + + clearComponentCreationData(dispatch) { + dispatch({ + type: actionTypes.COMPONENT_DATA_CLEAR + }); + }, + + closeComponentCreationModal(dispatch) { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }); + SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch); + }, + + deleteComponent(dispatch, {softwareProductId, componentId, version}) { + deleteSoftwareProductComponent(softwareProductId, componentId, version); + dispatch({ + type: actionTypes.COMPONENT_DELETE, + componentId: componentId + }); + }, + + + }; export default SoftwareProductComponentsActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js index 9307b099ed..35633b65cf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js @@ -18,7 +18,13 @@ import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ COMPONENTS_LIST_UPDATE: null, COMPONENTS_LIST_EDIT: null, - COMPONENT_LOAD: null + COMPONENT_UPDATE: null, + COMPONENT_DATA_CHANGED: null, + COMPONENT_DATA_CLEAR: null, + COMPONENT_QUESTIONNAIRE_UPDATE: null, + COMPONENT_DELETE: null, + COMPONENT_LOAD: null, + COMPONENT_CREATE_OPEN: null }); export const storageConstants = keyMirror({ @@ -30,16 +36,20 @@ export const storageConstants = keyMirror({ export const forms = keyMirror({ ALL_SPC_FORMS: null, - NIC_EDIT_FORM: null + NIC_EDIT_FORM: null, + CREATE_FORM: null, + IMAGE_EDIT_FORM: null }); export const COMPONENTS_QUESTIONNAIRE = 'component'; +export const COMPONENTS_COMPUTE_QUESTIONNAIRE = 'compute'; export const navigationItems = keyMirror({ STORAGE: 'Storage', PROCESS_DETAILS: 'Process Details', MONITORING: 'Monitoring', NETWORK: 'Network', + IMAGES: 'Images', COMPUTE: 'Compute', LOAD_BALANCING: 'High Availability & Load Balancing' }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js index f789a92c6f..bd4c2fa884 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js @@ -14,21 +14,16 @@ * permissions and limitations under the License. */ import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + 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); +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import SoftwareProductComponentsActionHelper from '../components/SoftwareProductComponentsActionHelper.js'; +import {actionTypes as globalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; - return { - currentSoftwareProduct, - isReadOnlyMode, - componentsList - }; +const generateMessage = (name) => { + return i18n(`Are you sure you want to delete ${name}?`); }; @@ -36,8 +31,21 @@ const mapActionToProps = (dispatch) => { return { onComponentSelect: ({id: softwareProductId, componentId, version}) => { OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); - } + }, + onAddComponent: (softwareProductId) => SoftwareProductActionHelper.addComponent(dispatch, {softwareProductId, modalClassName: 'create-vfc-modal'}), + onDeleteComponent: (component, softwareProductId, version) => dispatch({ + type: globalModalActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateMessage(component.displayName), + onConfirmed: ()=>SoftwareProductComponentsActionHelper.deleteComponent(dispatch, + { + softwareProductId, + componentId: component.id, + version + }) + } + }) }; }; -export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsListView); +export default connect(null, 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 index c7aaca5573..92211e0fd2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js @@ -22,6 +22,8 @@ export default (state = [], action) => { 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)]; + case actionTypes.COMPONENT_DELETE: + return state.filter(component => component.id !== action.componentId); 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 index c28831fbde..a2a1964299 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx @@ -40,11 +40,11 @@ class SoftwareProductComponentsListView extends React.Component { }; render() { - let {componentsList = []} = this.props; + let {componentsList = [], isManual} = this.props; return ( <div className=''> { - componentsList.length > 0 && this.renderComponents() + (componentsList.length > 0 || isManual) && this.renderComponents() } </div> ); @@ -52,15 +52,16 @@ class SoftwareProductComponentsListView extends React.Component { renderComponents() { const {localFilter} = this.state; - let {isReadOnlyMode} = this.props; - + const {isManual, onAddComponent, isReadOnlyMode, currentSoftwareProduct: {id: softwareProductId}, componentsList } = this.props; return ( <ListEditorView title={i18n('Virtual Function Components')} filterValue={localFilter} placeholder={i18n('Filter Components')} onFilter={value => this.setState({localFilter: value})} - isReadOnlyMode={isReadOnlyMode} + isReadOnlyMode={isReadOnlyMode || !!this.filterList().length} + plusButtonTitle={i18n('Add Component')} + onAdd={isManual && componentsList.length === 0 ? () => onAddComponent(softwareProductId) : false} twoColumns> {this.filterList().map(component => this.renderComponentsListItem(component))} </ListEditorView> @@ -69,11 +70,12 @@ class SoftwareProductComponentsListView extends React.Component { renderComponentsListItem(component) { let {id: componentId, name, displayName, description = ''} = component; - let {currentSoftwareProduct: {id, version}, onComponentSelect} = this.props; + let {currentSoftwareProduct: {id, version}, onComponentSelect, isManual, isReadOnlyMode, onDeleteComponent} = this.props; return ( <ListEditorItemView key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} className='list-editor-item-view' + onDelete={isManual && !isReadOnlyMode ? () => onDeleteComponent(component, id, version) : false} onSelect={() => onComponentSelect({id, componentId, version})}> <ListEditorItemViewField> <div className='name'>{displayName}</div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js new file mode 100644 index 0000000000..02c09fbdf8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js @@ -0,0 +1,169 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes} from './computeComponents/computeFlavor/ComputeFlavorConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes as globalModalActionTypes, modalSizes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {COMPONENTS_COMPUTE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; + +function baseUrl(softwareProductId, componentId, version) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/compute-flavors`; +} + +function baseUrlVSPLevel(softwareProductId, version){ + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/compute-flavors`; +} + +function fetchComputesList(softwareProductId, componentId, version){ + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId, version)}`); +} + +function fetchComputesListForVSP(softwareProductId, version){ + return RestAPIUtil.fetch(`${baseUrlVSPLevel(softwareProductId, version)}`); +} + +function fetchCompute(softwareProductId, componentId, computeId, version) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId, version)}/${computeId}`); +} + +function fetchComputeQuestionnaire({softwareProductId, componentId, computeId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId, version)}/${computeId}/questionnaire`); +} + +function postCompute({softwareProductId, componentId, compute, version}) { + return RestAPIUtil.post(baseUrl(softwareProductId, componentId, version), compute); +} + +function putCompute({softwareProductId, componentId, compute, version}) { + const computeData = { + name: compute.name, + description: compute.description + }; + return RestAPIUtil.put(`${baseUrl(softwareProductId, componentId, version)}/${compute.id}`, computeData); +} + +function putComputeQuestionnaire({softwareProductId, componentId, computeId, qdata, version}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, componentId, version)}/${computeId}/questionnaire`, qdata); +} + +function deleteCompute({softwareProductId, componentId, computeId, version}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId, version)}/${computeId}`); +} + + +const ComputeFlavorActionHelper = { + openComputeEditor(dispatch, {props}) { + dispatch({ + type: actionTypes.computeEditor.LOAD_EDITOR_DATA, + compute: props.compute || {} + }); + dispatch({ + type: globalModalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.COMPONENT_COMPUTE_FLAVOR_EDITOR, + modalClassName: `compute-flavor-editor-modal-${props.compute ? 'edit' : 'create'}`, + modalComponentProps: {...props, size: props.compute ? modalSizes.LARGE : undefined, dialogClassName:'compute-flavor-editor-modal'}, + title: `${props.compute ? i18n('Edit Compute Flavor') : i18n('Create New Compute Flavor')}` + } + }); + }, + + closeComputeEditor(dispatch){ + dispatch({ + type: globalModalActionTypes.GLOBAL_MODAL_CLOSE + }); + dispatch({ + type: actionTypes.computeEditor.CLEAR_DATA + }); + }, + + fetchComputesList(dispatch, {softwareProductId, componentId, version}) { + return fetchComputesList(softwareProductId, componentId, version).then(response => dispatch({ + type: actionTypes.COMPUTE_FLAVORS_LIST_LOADED, + response + })); + }, + + fetchComputesListForVSP(dispatch, {softwareProductId, version}) { + return fetchComputesListForVSP(softwareProductId, version).then(response => dispatch({ + type: actionTypes.COMPUTE_FLAVORS_LIST_LOADED, + response + })); + }, + + loadComputeData({softwareProductId, componentId, computeId, version}) { + return fetchCompute(softwareProductId, componentId, computeId, version); + }, + + loadComputeQuestionnaire(dispatch, {softwareProductId, componentId, computeId, version}) { + return fetchComputeQuestionnaire({softwareProductId, componentId, computeId, version}).then(response => + ValidationHelper.qDataLoaded(dispatch, {qName: COMPONENTS_COMPUTE_QUESTIONNAIRE ,response: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + }}) + ); + }, + + loadCompute(dispatch, {softwareProductId, componentId, version, computeId, isReadOnlyMode}){ + return ComputeFlavorActionHelper.loadComputeData({softwareProductId, componentId, computeId, version}).then(({data}) => + ComputeFlavorActionHelper.loadComputeQuestionnaire(dispatch, {softwareProductId, componentId, computeId, version}).then(() => + ComputeFlavorActionHelper.openComputeEditor(dispatch, {props: {softwareProductId, componentId, version, isReadOnlyMode, compute: {id: computeId, ...data}}}) + )); + }, + + saveComputeDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data: compute, qdata, version}) { + ComputeFlavorActionHelper.closeComputeEditor(dispatch); + if(compute.id) { + return Promise.all([ + putComputeQuestionnaire({softwareProductId, componentId, computeId: compute.id, qdata, version}), + putCompute({softwareProductId, componentId, compute, version}).then(() => { + dispatch({ + type: actionTypes.COMPUTE_LIST_EDIT, + compute + }); + }) + ]); + } + else { + return postCompute({softwareProductId, componentId, compute, version}).then(response => + dispatch({ + type: actionTypes.ADD_COMPUTE, + compute: { + ...compute, + id: response.id, + componentId + } + }) + ); + } + }, + + deleteCompute(dispatch, {softwareProductId, componentId, computeId, version}) { + return deleteCompute({softwareProductId, componentId, computeId, version}).then(() => dispatch({ + type: actionTypes.DELETE_COMPUTE, + computeId + })); + } +}; + +export default ComputeFlavorActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js index e97477b54d..bb8df29b82 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js @@ -19,18 +19,23 @@ import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwarePr import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {onboardingMethod} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -const mapStateToProps = ({softwareProduct}) => { +const mapStateToProps = ({softwareProduct, currentScreen: {props: {softwareProductId, componentId}}}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; + let {componentEditor: {qdata, dataMap, qgenericFieldInfo}, computeFlavor: {computesList: computeFlavorsList}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { qdata, dataMap, qgenericFieldInfo, - isReadOnlyMode + isReadOnlyMode, + softwareProductId, + componentId, + computeFlavorsList, + isManual: currentVSP.onboardingMethod === onboardingMethod.MANUAL }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx index 8c197f0d49..dd524a35f3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx @@ -15,9 +15,9 @@ */ import React from 'react'; import Form from 'nfvo-components/input/validation/Form.jsx'; -import VmSizing from './computeComponents/VmSizing.jsx'; import NumberOfVms from './computeComponents/NumberOfVms.jsx'; import GuestOs from './computeComponents/GuestOs.jsx'; +import ComputeFlavors from './computeComponents/ComputeFlavors.js'; import Validator from 'nfvo-utils/Validator.js'; class SoftwareProductComponentComputeView extends React.Component { @@ -26,13 +26,15 @@ class SoftwareProductComponentComputeView extends React.Component { dataMap: React.PropTypes.object, qgenericFieldInfo: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, + isManual: React.PropTypes.bool, onQDataChanged: React.PropTypes.func.isRequired, qValidateData: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired }; render() { - let {qdata, dataMap, qgenericFieldInfo, isReadOnlyMode, onQDataChanged, qValidateData, onSubmit} = this.props; + let {softwareProductId, componentId, version, qdata, dataMap, qgenericFieldInfo, isReadOnlyMode, onQDataChanged, qValidateData, + onSubmit, computeFlavorsList, isManual} = this.props; return ( <div className='vsp-component-questionnaire-view'> @@ -44,11 +46,12 @@ class SoftwareProductComponentComputeView extends React.Component { onSubmit={() => onSubmit({qdata})} className='component-questionnaire-validation-form' isReadOnlyMode={isReadOnlyMode} > - <VmSizing onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> <NumberOfVms onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} qValidateData={qValidateData} customValidations={{'compute/numOfVMs/maximum' : this.validateMax, 'compute/numOfVMs/minimum': this.validateMin}} /> <GuestOs onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> + <ComputeFlavors computeFlavorsList={computeFlavorsList} softwareProductId={softwareProductId} componentId={componentId} + version={version} isReadOnlyMode={isReadOnlyMode} isManual={isManual}/> </Form> } </div> ); @@ -60,12 +63,24 @@ class SoftwareProductComponentComputeView extends React.Component { validateMin(value, state) { let maxVal = state.dataMap['compute/numOfVMs/maximum']; - return Validator.validateItem(value,maxVal,'maximum'); + // we are allowed to have an empty maxval, that will allow all minvals. + // if we do not have a minval than there is no point to check it either. + if (value === undefined || maxVal === undefined) { + return { isValid: true, errorText: '' }; + } else { + return Validator.validateItem(value, maxVal,'maximum'); + } } validateMax(value, state) { let minVal = state.dataMap['compute/numOfVMs/minimum']; - return Validator.validateItem(value,minVal,'minimum'); + if (minVal === undefined ) { + // having no minimum is the same as 0, maximum value doesn't need to be checked + // against it. + return { isValid: true, errorText: '' }; + } else { + return Validator.validateItem(value,minVal,'minimum'); + } } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/ComputeFlavors.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/ComputeFlavors.js new file mode 100644 index 0000000000..c72d42c11f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/ComputeFlavors.js @@ -0,0 +1,116 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ComputeFlavorActionHelper from 'sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) => { + return { + onAddComputeClick: (isReadOnlyMode) => ComputeFlavorActionHelper.openComputeEditor(dispatch, {props: {softwareProductId, componentId, isReadOnlyMode, version}}), + onEditCompute: ({computeId, isReadOnlyMode}) => ComputeFlavorActionHelper.loadCompute(dispatch, {softwareProductId, componentId, version, computeId, isReadOnlyMode}), + onDeleteCompute: ({id, name}) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete "${name}"?`), + onConfirmed: () => ComputeFlavorActionHelper.deleteCompute(dispatch, {softwareProductId, componentId, computeId: id, version}) + } + }) + }; +}; + +const computeItemPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string +}); + +class ComputeFlavors extends React.Component { + + static propTypes = { + isReadOnlyMode: React.PropTypes.bool, + isManual: React.PropTypes.bool, + onAddComputeClick: React.PropTypes.func, + computeFlavorsList: React.PropTypes.arrayOf(computeItemPropType) + }; + + state = { + localFilter: '' + }; + + render() { + const {localFilter} = this.state; + const {isReadOnlyMode, isManual, onAddComputeClick, onEditCompute, onDeleteCompute} = this.props; + return ( + <div className='computes-list'> + <ListEditorView + title={i18n('Computes')} + plusButtonTitle={i18n('Add Compute')} + onAdd={isManual ? () => onAddComputeClick(isReadOnlyMode) : null} + isReadOnlyMode={isReadOnlyMode} + onFilter={isManual ? value => this.setState({localFilter: value}) : null} + filterValue={localFilter} + twoColumns> + {this.filterList().map(computeItem => + <ComputeItem key={computeItem.id} + computeItem={computeItem} isReadOnlyMode={isReadOnlyMode} isManual={isManual} + onEditCompute={onEditCompute} onDeleteCompute={onDeleteCompute}/>) + } + </ListEditorView> + </div> + ); + } + + filterList() { + const {computeFlavorsList = []} = this.props; + + const {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return computeFlavorsList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return computeFlavorsList; + } + } +} + +const ComputeItem = ({computeItem, isReadOnlyMode, isManual, onEditCompute, onDeleteCompute}) => { + const {id, name, description} = computeItem; + return ( + <ListEditorItemView + key={'item_' + id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditCompute({computeId: id, isReadOnlyMode})} + onDelete={isManual ? () => onDeleteCompute({id, name}) : null}> + + <div className='list-editor-item-view-field'> + <div className='name'>{name}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='description'>{description}</div> + </div> + </ListEditorItemView> + ); +}; + +export default connect(null, mapActionsToProps, null, {withRef: true})(ComputeFlavors); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx index 7a730d6f94..16bf599834 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx @@ -24,17 +24,6 @@ const GuestOs = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { return( <div> <GridSection title={i18n('Guest OS')} > - <GridItem colSpan={2}> - <Input - data-test-id='guestOS-name' - label={i18n('Guest OS')} - type='text' - onChange={(tools) => onQDataChanged({'compute/guestOS/name' : tools})} - isValid={qgenericFieldInfo['compute/guestOS/name'].isValid} - errorText={qgenericFieldInfo['compute/guestOS/name'].errorText} - value={dataMap['compute/guestOS/name']} /> - </GridItem> - <GridItem colSpan={2}/> <GridItem> <div className='vertical-flex'> <label key='label' className='control-label'>{i18n('OS Bit Size')}</label> @@ -58,6 +47,16 @@ const GuestOs = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { <GridItem colSpan={2}/> <GridItem colSpan={2}> <Input + data-test-id='guestOS-name' + label={i18n('Guest OS')} + type='textarea' + onChange={(tools) => onQDataChanged({'compute/guestOS/name' : tools})} + isValid={qgenericFieldInfo['compute/guestOS/name'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/name'].errorText} + value={dataMap['compute/guestOS/name']} /> + </GridItem> + <GridItem colSpan={2}> + <Input data-test-id='guestOS-tools' type='textarea' label={i18n('Guest OS Tools:')} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx index efeedc653e..ddde4391d9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx @@ -45,44 +45,6 @@ const NumberOfVms = ({qgenericFieldInfo, dataMap, onQDataChanged, qValidateData, errorText={qgenericFieldInfo['compute/numOfVMs/maximum'].errorText} value={dataMap['compute/numOfVMs/maximum']} /> </GridItem> - <GridItem> - <Input - data-test-id='numOfVMs-CpuOverSubscriptionRatio' - label={i18n('CPU Oversubscription Ratio')} - type='select' - groupClassName='bootstrap-input-options' - className='input-options-select' - isValid={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].isValid} - errorText={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].errorText} - value={dataMap['compute/numOfVMs/CpuOverSubscriptionRatio']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'compute/numOfVMs/CpuOverSubscriptionRatio' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].enum.map(cpuOSR => <option value={cpuOSR.enum} key={cpuOSR.enum}>{cpuOSR.title}</option>)} - </Input> - </GridItem> - <GridItem> - <Input - data-test-id='numOfVMs-MemoryRAM' - type='select' - label={i18n('Memory - RAM')} - groupClassName='bootstrap-input-options' - className='input-options-select' - isValid={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].isValid} - errorText={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].errorText} - value={dataMap['compute/numOfVMs/MemoryRAM']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'compute/numOfVMs/MemoryRAM' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].enum.map(mRAM => <option value={mRAM.enum} key={mRAM.enum}>{mRAM.title}</option>)} - </Input> - </GridItem> </GridSection> ); }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx deleted file mode 100644 index 39f84807a2..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -import React from 'react'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Input from 'nfvo-components/input/validation/Input.jsx'; -import GridSection from 'nfvo-components/grid/GridSection.jsx'; -import GridItem from 'nfvo-components/grid/GridItem.jsx'; -const VmSizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { - return( - <GridSection title={i18n('VM Sizing')}> - <GridItem> - <Input - data-test-id='numOfCPUs' - type='number' - label={i18n('Number of CPUs')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/numOfCPUs' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].errorText} - value={dataMap['compute/vmSizing/numOfCPUs']} /> - </GridItem> - <GridItem> - <Input - data-test-id='fileSystemSizeGB' - type='number' - label={i18n('File System Size (GB)')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/fileSystemSizeGB' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].errorText} - value={dataMap['compute/vmSizing/fileSystemSizeGB']} /> - </GridItem> - <GridItem> - <Input - data-test-id='persistentStorageVolumeSize' - type='number' - label={i18n('Persistent Storage/Volume Size (GB)')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/persistentStorageVolumeSize' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].errorText} - value={dataMap['compute/vmSizing/persistentStorageVolumeSize']} /> - </GridItem> - <GridItem> - <Input - data-test-id='IOOperationsPerSec' - type='number' - label={i18n('I/O Operations (per second)')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/IOOperationsPerSec' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].errorText} - value={dataMap['compute/vmSizing/IOOperationsPerSec']} /> - </GridItem> - </GridSection> - ); -}; - -export default VmSizing; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorConstants.js new file mode 100644 index 0000000000..41728eefb0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorConstants.js @@ -0,0 +1,32 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const COMPUTE_FLAVOR_FORM = 'COMPUTE_FLAVOR_FORM'; + +export const actionTypes = keyMirror({ + ADD_COMPUTE: null, + COMPUTE_FLAVORS_LIST_LOADED: null, + COMPUTE_LIST_EDIT: null, + EDIT_COMPUTE_FLAVOR: null, + DELETE_COMPUTE: null, + CONFIRM_DELETE_COMPUTE: null, + computeEditor: { + LOAD_EDITOR_DATA: null, + CLEAR_DATA: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js new file mode 100644 index 0000000000..caec0702fd --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js @@ -0,0 +1,55 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import ComputeFlavorEditorView from './ComputeFlavorEditorView.jsx'; +import {COMPUTE_FLAVOR_FORM} from './ComputeFlavorConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ComputeFlavorActionHelper from 'sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {COMPONENTS_COMPUTE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {onboardingMethod} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductEditor, softwareProductComponents: {computeFlavor: {computeEditor = {}}}}}) => { + const {data: currentSoftwareProduct = {}} = softwareProductEditor; + const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {data , qdata, qgenericFieldInfo, dataMap, genericFieldInfo, formReady} = computeEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + return { + data, + qdata, + qgenericFieldInfo, + dataMap, + genericFieldInfo, + isReadOnlyMode, + isFormValid, + formReady, + isManual: currentSoftwareProduct.onboardingMethod === onboardingMethod.MANUAL + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) => { + return { + onDataChanged: deltaData => ValidationHelper.dataChanged(dispatch, {deltaData, formName: COMPUTE_FLAVOR_FORM}), + onQDataChanged: deltaData => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_COMPUTE_QUESTIONNAIRE}), + onCancel: () => ComputeFlavorActionHelper.closeComputeEditor(dispatch), + onSubmit: ({data, qdata}) => ComputeFlavorActionHelper.saveComputeDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata, version}), + onValidateForm: () => ValidationHelper.validateForm(dispatch, COMPUTE_FLAVOR_FORM) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ComputeFlavorEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditorView.jsx new file mode 100644 index 0000000000..8f8a504629 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditorView.jsx @@ -0,0 +1,96 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import VmSizing from './VmSizing.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +class ComputeEditorView extends React.Component { + + static propTypes = { + data: React.PropTypes.object, + qdata: React.PropTypes.object, + qschema: React.PropTypes.object, + isReadOnlyMode: React.PropTypes.bool, + isManual: React.PropTypes.bool, + onDataChanged: React.PropTypes.func.isRequired, + onQDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {data = {}, qdata = {}, qgenericFieldInfo, dataMap, genericFieldInfo, isReadOnlyMode, isManual, isFormValid, formReady, + onDataChanged, onQDataChanged, onSubmit, onCancel, onValidateForm} = this.props; + const {id, name, description} = data; + const edittingComputeMode = Boolean(id); + + return ( + <div className='vsp-component-computeFlavor-view'> + {genericFieldInfo && <Form + ref={(form) => { + this.form = form; + }} + hasButtons={true} + onSubmit={ () => onSubmit({data, qdata}) } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={isFormValid} + formReady={formReady} + onValidateForm={() => onValidateForm() } + className='component-questionnaire-validation-form' + submitButtonText={edittingComputeMode ? i18n('Save') : i18n('Create')}> + <GridSection> + <GridItem colSpan={edittingComputeMode ? 2 : 4}> + <Input + disabled={!isManual} + data-test-id='name' + type='text' + label={i18n('Flavor Name')} + value={name} + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo['name'].isValid} + errorText={genericFieldInfo['name'].errorText} + isRequired/> + </GridItem> + <GridItem colSpan={edittingComputeMode ? 2 : 4}> + <Input + data-test-id='description' + type='textarea' + label={i18n('Description')} + value={description} + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo['description'].isValid} + errorText={genericFieldInfo['description'].errorText}/> + </GridItem> + </GridSection> + {edittingComputeMode && <VmSizing qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged}/>} + </Form> + } + </div> + ); + } + + save(){ + return this.form.handleFormSubmit(new Event('dummy')); + } +} + +export default ComputeEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js new file mode 100644 index 0000000000..6c02f36c90 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js @@ -0,0 +1,33 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {actionTypes} from './ComputeFlavorConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.COMPUTE_FLAVORS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.ADD_COMPUTE: + return [...state, action.compute]; + case actionTypes.COMPUTE_LIST_EDIT: + const indexForEdit = state.findIndex(({id}) => id === action.compute.id); + return [...state.slice(0, indexForEdit), action.compute, ...state.slice(indexForEdit + 1)]; + case actionTypes.DELETE_COMPUTE: + return state.filter(({id}) => id !== action.computeId); + default: + return state; + } +};
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js new file mode 100644 index 0000000000..a476f85a19 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js @@ -0,0 +1,45 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes, COMPUTE_FLAVOR_FORM} from './ComputeFlavorConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.computeEditor.LOAD_EDITOR_DATA: + return { + ...state, + formName: COMPUTE_FLAVOR_FORM, + data: action.compute, + formReady: null, + genericFieldInfo: { + name: { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true }] + }, + description: { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 300}] + } + } + }; + case actionTypes.computeEditor.CLEAR_DATA: + return {}; + default: + return state; + } +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/VmSizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/VmSizing.jsx new file mode 100644 index 0000000000..8b30468362 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/VmSizing.jsx @@ -0,0 +1,106 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +const VmSizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={i18n('VM Sizing')}> + <GridItem> + <Input + data-test-id='numOfCPUs' + type='number' + label={i18n('Number of CPUs')} + onChange={(tools) => onQDataChanged({'vmSizing/numOfCPUs' : tools})} + isValid={qgenericFieldInfo['vmSizing/numOfCPUs'].isValid} + errorText={qgenericFieldInfo['vmSizing/numOfCPUs'].errorText} + value={dataMap['vmSizing/numOfCPUs']} /> + </GridItem> + <GridItem> + <Input + data-test-id='fileSystemSizeGB' + type='number' + label={i18n('File System Size (GB)')} + onChange={(tools) => onQDataChanged({'vmSizing/fileSystemSizeGB' : tools})} + isValid={qgenericFieldInfo['vmSizing/fileSystemSizeGB'].isValid} + errorText={qgenericFieldInfo['vmSizing/fileSystemSizeGB'].errorText} + value={dataMap['vmSizing/fileSystemSizeGB']} /> + </GridItem> + <GridItem> + <Input + data-test-id='persistentStorageVolumeSize' + type='number' + label={i18n('Persistent Storage/Volume Size (GB)')} + onChange={(tools) => onQDataChanged({'vmSizing/persistentStorageVolumeSize' : tools})} + isValid={qgenericFieldInfo['vmSizing/persistentStorageVolumeSize'].isValid} + errorText={qgenericFieldInfo['vmSizing/persistentStorageVolumeSize'].errorText} + value={dataMap['vmSizing/persistentStorageVolumeSize']} /> + </GridItem> + <GridItem> + <Input + data-test-id='ioOperationsPerSec' + type='number' + label={i18n('I/O Operations (per second)')} + onChange={(tools) => onQDataChanged({'vmSizing/ioOperationsPerSec' : tools})} + isValid={qgenericFieldInfo['vmSizing/ioOperationsPerSec'].isValid} + errorText={qgenericFieldInfo['vmSizing/ioOperationsPerSec'].errorText} + value={dataMap['vmSizing/ioOperationsPerSec']} /> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-cpuOverSubscriptionRatio' + label={i18n('CPU Oversubscription Ratio')} + type='select' + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['vmSizing/cpuOverSubscriptionRatio'].isValid} + errorText={qgenericFieldInfo['vmSizing/cpuOverSubscriptionRatio'].errorText} + value={dataMap['vmSizing/cpuOverSubscriptionRatio']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'vmSizing/cpuOverSubscriptionRatio' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['vmSizing/cpuOverSubscriptionRatio'].enum.map(cpuOSR => <option value={cpuOSR.enum} key={cpuOSR.enum}>{cpuOSR.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-memoryRAM' + type='select' + label={i18n('Memory - RAM')} + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['vmSizing/memoryRAM'].isValid} + errorText={qgenericFieldInfo['vmSizing/memoryRAM'].errorText} + value={dataMap['vmSizing/memoryRAM']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'vmSizing/memoryRAM' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['vmSizing/memoryRAM'].enum.map(mRAM => <option value={mRAM.enum} key={mRAM.enum}>{mRAM.title}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +export default VmSizing; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js new file mode 100644 index 0000000000..e85b6b6504 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js @@ -0,0 +1,50 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentCreationView from './SoftwareProductComponentCreationView.jsx'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {forms} from '../SoftwareProductComponentsConstants.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductComponents: {componentEditor: {data, genericFieldInfo, formReady}}, softwareProductEditor: {data: {version}}} = softwareProduct; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + return { + data, + genericFieldInfo, + formReady, + isFormValid, + version + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + return { + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.CREATE_FORM}), + //onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}), + onSubmit: (componentData, version) => { + return SoftwareProductComponentsActionHelper.createSoftwareProductComponent(dispatch, + {softwareProductId, componentData, version}); + }, + onCancel: () => SoftwareProductComponentsActionHelper.closeComponentCreationModal(dispatch), + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) + }; + +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreationView.jsx new file mode 100644 index 0000000000..55bcc818f5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreationView.jsx @@ -0,0 +1,79 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {forms} from '../SoftwareProductComponentsConstants.js'; + +class ComponentCreationView extends React.Component { + render() { + let {data = {}, onDataChanged, onCancel, genericFieldInfo} = this.props; + let {displayName, description} = data; + return( + <div> + { + genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + submitButtonText={i18n('Create')} + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(forms.CREATE_FORM) } + className='entitlement-pools-form'> + <GridSection> + <GridItem colSpan={4}> + <Input + data-test-id='name' + onChange={displayName => onDataChanged({displayName})} + label={i18n('Name')} + isRequired={true} + isValid={genericFieldInfo.displayName.isValid} + errorText={genericFieldInfo.displayName.errorText} + value={displayName} + type='text'/> + </GridItem> + <GridItem colSpan={4}> + <Input + label={i18n('Description')} + onChange={description => onDataChanged({description})} + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + data-test-id='description' + type='textarea'/> + </GridItem> + </GridSection> + </Form> + } + </div> + ); + } + + submit() { + const {onSubmit, data, version} = this.props; + onSubmit(data, version); + } +} + +export default ComponentCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js index 34374aa7fb..7b4135028b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js @@ -22,6 +22,7 @@ import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/Soft import {forms, COMPONENTS_QUESTIONNAIRE} from '../SoftwareProductComponentsConstants.js'; +import {onboardingMethod} from '../../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct}) => { @@ -34,6 +35,7 @@ export const mapStateToProps = ({softwareProduct}) => { componentData, qdata, isReadOnlyMode, + isManual: currentVSP.onboardingMethod === onboardingMethod.MANUAL, genericFieldInfo, qGenericFieldInfo, dataMap, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx index e4595f97d6..6aa51d1609 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx @@ -21,7 +21,7 @@ import Form from 'nfvo-components/input/validation/Form.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isReadOnlyMode, genericFieldInfo}) => ( +const GeneralSection = ({onDataChanged, displayName, vfcCode, nfcFunction, description, isReadOnlyMode, genericFieldInfo, isManual}) => ( <GridSection title={i18n('General')}> {/* disabled until backend will be ready to implement it <div className='validation-input-wrapper'> @@ -37,9 +37,9 @@ const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isRea data-test-id='name' label={i18n('Name')} value={displayName} - disabled={true} + disabled={!isManual || isReadOnlyMode} type='text'/> - <Input + {!isManual && <Input data-test-id='vfcCode' label={i18n('Naming Code')} value={vfcCode} @@ -47,6 +47,15 @@ const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isRea errorText={genericFieldInfo.vfcCode.errorText} onChange={vfcCode => onDataChanged({vfcCode})} disabled={isReadOnlyMode} + type='text'/> } + <Input + data-test-id='nfcFunction' + label={i18n('Function')} + value={nfcFunction} + isValid={genericFieldInfo.nfcFunction.isValid} + errorText={genericFieldInfo.nfcFunction.errorText} + onChange={nfcFunction => onDataChanged({nfcFunction})} + disabled={isReadOnlyMode} type='text'/> </GridItem> <GridItem colSpan={2}> @@ -63,7 +72,7 @@ const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isRea </GridItem> <GridItem /> </GridSection> - ); +); const HypervisorSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( <GridSection title={i18n('Hypervisor')}> @@ -110,64 +119,26 @@ const HypervisorSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( ); const ImageSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( - <GridSection title={i18n('Image')}> - <GridItem> - <Input - data-test-id='format' - label={i18n('Image format')} - type='select' - className='input-options-select' - groupClassName='bootstrap-input-options' - isValid={qgenericFieldInfo['general/image/format'].isValid} - errorText={qgenericFieldInfo['general/image/format'].errorText} - value={dataMap['general/image/format']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'general/image/format' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['general/image/format'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} - </Input> - </GridItem> - <GridItem> - <Input - data-test-id='providedBy' - label={i18n('Image provided by')} - type='select' - className='input-options-select' - groupClassName='bootstrap-input-options' - isValid={qgenericFieldInfo['general/image/providedBy'].isValid} - errorText={qgenericFieldInfo['general/image/providedBy'].errorText} - value={dataMap['general/image/providedBy']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'general/image/providedBy' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['general/image/providedBy'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} - </Input> - </GridItem> + <GridSection title={i18n('Disk')}> <GridItem> <Input data-test-id='bootDiskSizePerVM' - onChange={(bootDiskSizePerVM) => onQDataChanged({'general/image/bootDiskSizePerVM' : bootDiskSizePerVM})} + onChange={(bootDiskSizePerVM) => onQDataChanged({'general/disk/bootDiskSizePerVM' : bootDiskSizePerVM})} label={i18n('Size of boot disk per VM (GB)')} type='number' - isValid={qgenericFieldInfo['general/image/bootDiskSizePerVM'].isValid} - errorText={qgenericFieldInfo['general/image/bootDiskSizePerVM'].errorText} - value={dataMap['general/image/bootDiskSizePerVM']}/> + isValid={qgenericFieldInfo['general/disk/bootDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/disk/bootDiskSizePerVM'].errorText} + value={dataMap['general/disk/bootDiskSizePerVM']}/> </GridItem> <GridItem> <Input data-test-id='ephemeralDiskSizePerVM' - onChange={(ephemeralDiskSizePerVM) => onQDataChanged({'general/image/ephemeralDiskSizePerVM' : ephemeralDiskSizePerVM})} + onChange={(ephemeralDiskSizePerVM) => onQDataChanged({'general/disk/ephemeralDiskSizePerVM' : ephemeralDiskSizePerVM})} label={i18n('Size of ephemeral disk per VM (GB)')} type='number' - isValid={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].isValid} - errorText={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].errorText} - value={dataMap['general/image/ephemeralDiskSizePerVM']}/> + isValid={qgenericFieldInfo['general/disk/ephemeralDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/disk/ephemeralDiskSizePerVM'].errorText} + value={dataMap['general/disk/ephemeralDiskSizePerVM']}/> </GridItem> </GridSection> ); @@ -257,7 +228,7 @@ const CloneSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( class SoftwareProductComponentsGeneralView extends React.Component { render() { - let {onQDataChanged, onDataChanged, genericFieldInfo, dataMap, qGenericFieldInfo, componentData: {displayName, vfcCode, description}, isReadOnlyMode} = this.props; + let {isManual, onQDataChanged, onDataChanged, genericFieldInfo, dataMap, qGenericFieldInfo, componentData: {displayName, vfcCode, nfcFunction, description}, isReadOnlyMode} = this.props; return( <div className='vsp-components-general'> <div className='general-data'> @@ -271,7 +242,9 @@ class SoftwareProductComponentsGeneralView extends React.Component { onDataChanged={onDataChanged} displayName={displayName} vfcCode={vfcCode} + nfcFunction={nfcFunction} description={description} + isManual={isManual} isReadOnlyMode={isReadOnlyMode} genericFieldInfo={genericFieldInfo}/> <HypervisorSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js new file mode 100644 index 0000000000..34198281b7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js @@ -0,0 +1,169 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {IMAGE_QUESTIONNAIRE} from './SoftwareProductComponentsImageConstants.js'; +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; + +function baseUrl(softwareProductId, version, componentId) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/images`; +} + +function fetchImagesList({softwareProductId, componentId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); +} + +function fetchImage({softwareProductId, componentId, imageId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${imageId}`); +} + +function destroyImage({softwareProductId, componentId, version, imageId}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/${imageId}`); +} + +function createImage({softwareProductId, componentId, version, data}) { + return RestAPIUtil.post(baseUrl(softwareProductId, version, componentId), { + fileName: data.fileName + }); +} + +function fetchImageQuestionnaire({softwareProductId, componentId, imageId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${imageId}/questionnaire`); +} + +function saveImage({softwareProductId, version, componentId, image: {id, fileName}}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${id}`,{ + fileName + }); + +} + +function saveImageQuestionnaire({softwareProductId, componentId, version, imageId, qdata}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${imageId}/questionnaire`, qdata); +} + +const SoftwareProductComponentImagesActionHelper = { + fetchImagesList(dispatch, {softwareProductId, componentId, version}) { + dispatch({ + type: actionTypes.IMAGES_LIST_UPDATE, + response: [] + }); + + return fetchImagesList({softwareProductId, componentId, version}).then((response) => { + dispatch({ + type: actionTypes.IMAGES_LIST_UPDATE, + response: response.results, + componentId : componentId + }); + }); + }, + + deleteImage(dispatch, {softwareProductId, componentId, version, imageId}) { + return destroyImage({softwareProductId, componentId, version, imageId}).then(() => { + return SoftwareProductComponentImagesActionHelper.fetchImagesList(dispatch, {softwareProductId, componentId, version}); + }); + }, + + loadImageData({softwareProductId, componentId, imageId, version}) { + return fetchImage({softwareProductId, componentId, imageId, version}); + }, + + openEditImageEditor(dispatch, {image, softwareProductId, componentId, version, isReadOnlyMode, modalClassName}) { + return SoftwareProductComponentImagesActionHelper.loadImageData({softwareProductId, componentId, imageId: image.id, version}).then(({data}) => { + SoftwareProductComponentImagesActionHelper.loadImageQuestionnaire(dispatch, { + softwareProductId, + componentId, + imageId: image.id, + version + }).then(() => { + SoftwareProductComponentImagesActionHelper.openImageEditor(dispatch, { + softwareProductId, + componentId, + version, + isReadOnlyMode, + modalClassName, + image, + data + }); + }); + }); + }, + + openImageEditor(dispatch, {image = {}, data = {}, softwareProductId, componentId, version, isReadOnlyMode}) { + + let title = (image && image.id) ? i18n('Edit Image') : i18n('Create New Image'); + let className = (image && image.id) ? 'image-edit-editor-model' : 'image-new-editor-modal'; + + dispatch({ + type: actionTypes.ImageEditor.OPEN, + image: {...data, id: image.id} + }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR, + title: title, + modalComponentProps: {softwareProductId, componentId, version, isReadOnlyMode, dialogClassName:className} + } + }); + }, + + closeImageEditor(dispatch) { + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }); + + dispatch({ + type: actionTypes.ImageEditor.CLOSE + }); + }, + + loadImageQuestionnaire(dispatch, {softwareProductId, componentId, imageId, version}) { + return fetchImageQuestionnaire({softwareProductId, componentId, imageId, version}).then((response) => { + ValidationHelper.qDataLoaded(dispatch, {qName: IMAGE_QUESTIONNAIRE ,response: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + }}); + }); + }, + + saveImageDataAndQuestionnaire(dispatch, {softwareProductId, componentId, version, data, qdata}) { + SoftwareProductComponentImagesActionHelper.closeImageEditor(dispatch); + if (data !== null && data.id) { + // editor in edit mode + return Promise.all([ + saveImageQuestionnaire({softwareProductId, version, componentId, imageId: data.id, qdata}), + saveImage({softwareProductId, version, componentId, image: data}).then(() => { + return SoftwareProductComponentImagesActionHelper.fetchImagesList(dispatch, {softwareProductId, componentId, version}); + }) + ]); + } else { + // editor in create mode + createImage({softwareProductId, componentId, version, data}).then(() => { + return SoftwareProductComponentImagesActionHelper.fetchImagesList(dispatch, {softwareProductId, componentId, version}); + }); + } + } +}; +export default SoftwareProductComponentImagesActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js new file mode 100644 index 0000000000..6b6c9a30e5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js @@ -0,0 +1,27 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + IMAGES_LIST_UPDATE: null, + + ImageEditor: { + CLOSE: null, + OPEN: null + } +}); + +export const IMAGE_QUESTIONNAIRE = 'image'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js new file mode 100644 index 0000000000..49d891c9df --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js @@ -0,0 +1,63 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductComponentsImageActionHelper from './SoftwareProductComponentsImageActionHelper.js'; +import SoftwareProductComponentsImageEditorView from './SoftwareProductComponentsImageEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {IMAGE_QUESTIONNAIRE} from './SoftwareProductComponentsImageConstants.js'; + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + + let {images: {imageEditor = {}}} = softwareProductComponents; + let {data, qdata, genericFieldInfo, qgenericFieldInfo, dataMap, formReady} = imageEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {version, onboardingMethod} = currentSoftwareProduct; + let isManual = onboardingMethod === onboardingMethodTypes.MANUAL; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo) && ValidationHelper.checkFormValid(qgenericFieldInfo); + + return { + version, + currentSoftwareProduct, + isValidityData, + data, + qdata, + dataMap, + isFormValid, + formReady, + genericFieldInfo, + qgenericFieldInfo, + isReadOnlyMode, + isManual: isManual + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) => { + return { + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.IMAGE_EDIT_FORM}), + onSubmit: ({data, qdata}) => SoftwareProductComponentsImageActionHelper.saveImageDataAndQuestionnaire(dispatch, {softwareProductId, componentId, version, data, qdata}), + onCancel: () => SoftwareProductComponentsImageActionHelper.closeImageEditor(dispatch), + onValidateForm: () => ValidationHelper.validateForm(dispatch, forms.IMAGE_EDIT_FORM), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: IMAGE_QUESTIONNAIRE}), + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentsImageEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js new file mode 100644 index 0000000000..0ab785a97f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js @@ -0,0 +1,42 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.ImageEditor.CLOSE: + return {}; + case actionTypes.ImageEditor.OPEN: + return { + ...state, + data: { + ...action.image + }, + genericFieldInfo: { + 'fileName' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'validateName', data: true}] + } + }, + formName: forms.IMAGE_EDIT_FORM + }; + default: + return state; + } +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx new file mode 100644 index 0000000000..300f8edcc3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx @@ -0,0 +1,71 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; + +import FileDetails from './imagesEditorComponents/FileDetails.jsx'; +import ImageDetails from './imagesEditorComponents/ImageDetails.jsx'; + +class SoftwareProductComponentsImageEditorView extends React.Component { + static propTypes = { + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, genericFieldInfo, qgenericFieldInfo, dataMap, onDataChanged, isManual, onQDataChanged} = this.props; + let {id, fileName} = data; + let editingMode = Boolean(id); + return ( + <div> + {genericFieldInfo && <Form + ref={(form) => { this.form = form; }} + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={isFormValid} + formReady={formReady} + submitButtonText={editingMode ? i18n('Save') : i18n('Create')} + onValidateForm={() => onValidateForm() } + className='vsp-components-image-editor'> + <div className='editor-data'> + <FileDetails + editingMode={editingMode} + genericFieldInfo={genericFieldInfo} + qgenericFieldInfo={qgenericFieldInfo} + fileName={fileName} + onDataChanged={onDataChanged} + isManual={isManual} + dataMap={dataMap} + onQDataChanged={onQDataChanged}/> + {editingMode && <ImageDetails dataMap={dataMap}qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} + </div> + </Form>} + </div> + ); + } + submit() { + let {data, qdata, onSubmit, version} = this.props; + onSubmit({data, qdata, version}); + } +} + +export default SoftwareProductComponentsImageEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js new file mode 100644 index 0000000000..86c4e072d4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js @@ -0,0 +1,88 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import SoftwareProductComponentsImageListView from './SoftwareProductComponentsImageListView.jsx'; +import ImageHelper from './SoftwareProductComponentsImageActionHelper.js'; +import SoftwareProductComponentsImagesActionHelper from './SoftwareProductComponentsImageActionHelper.js'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; + +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + let {images: {imagesList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {version, onboardingMethod} = currentSoftwareProduct; + let isManual = onboardingMethod === onboardingMethodTypes.MANUAL; + + return { + version, + componentData, + qdata, + dataMap, + qgenericFieldInfo, + isValidityData, + imagesList, + isReadOnlyMode, + isManual : isManual + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: COMPONENTS_QUESTIONNAIRE}), + onAddImage: (version, isReadOnlyMode) => { + SoftwareProductComponentsImagesActionHelper.openImageEditor(dispatch, + {isReadOnlyMode, softwareProductId, + componentId, version} + );}, + onDeleteImage: ((image, version) => { + let shortenedFileName = (image.fileName.length > 40) ? image.fileName.substr(0,40) + '...' : image.fileName; + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data: { + msg: i18n(`Are you sure you want to delete "${shortenedFileName}"?`), + onConfirmed: () => ImageHelper.deleteImage(dispatch, { + softwareProductId, + componentId, + version, + imageId: image.id + }) + } + }); + }), + onEditImageClick: (image, version, isReadOnlyMode) => { + SoftwareProductComponentsImagesActionHelper.openEditImageEditor(dispatch, { + image, isReadOnlyMode, softwareProductId, componentId, version, modalClassName: 'image-modal-edit'} + ); + }, + onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + {softwareProductId, + vspComponentId: componentId, + qdata}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsImageListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListReducer.js new file mode 100644 index 0000000000..5dd2fb623b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListReducer.js @@ -0,0 +1,26 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + + case actionTypes.IMAGES_LIST_UPDATE: + return [...action.response]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx new file mode 100644 index 0000000000..ccf5b9d6b1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx @@ -0,0 +1,132 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; +import Input from'nfvo-components/input/validation/Input.jsx'; + +class SoftwareProductComponentsImageListView extends React.Component { + state = { + localFilter: '' + }; + + render() { + let {dataMap, onQDataChanged, isReadOnlyMode, qgenericFieldInfo} = this.props; + return( + <div className='vsp-components-image'> + <div className='image-data'> + <div> + { qgenericFieldInfo && <Form + formReady={null} + isValid={true} + onSubmit={() => this.save()} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + + <h3 className='section-title'>{i18n('Image')}</h3> + <div className='rows-section'> + <div className='row-flex-components'> + <div className='single-col'> + <Input + data-test-id='providedBy' + label={i18n('Image provided by')} + type='select' + isValid={qgenericFieldInfo['general/image/providedBy'].isValid} + errorText={qgenericFieldInfo['general/image/providedBy'].errorText} + value={dataMap['general/image/providedBy']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/image/providedBy' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + { qgenericFieldInfo['general/image/providedBy'].enum.map(proto => + <option value={proto.enum} key={proto.enum}>{proto.title}</option>) } + </Input> + </div> + <div className='empty-two-col' /> + </div> + </div> + + </Form> } + </div> + </div> + {this.renderImagesList()} + </div> + ); + }; + + renderImagesList() { + const {localFilter} = this.state; + let {isReadOnlyMode, onAddImage, isManual, version} = this.props; + + return ( + <ListEditorView + title={i18n('Images')} + filterValue={localFilter} + placeholder={i18n('Filter Images by Name')} + isReadOnlyMode={isReadOnlyMode} + onFilter={value => this.setState({localFilter: value})} + onAdd={isManual ? () => onAddImage(version, isReadOnlyMode) : null} + plusButtonTitle={i18n('Add Image')} + twoColumns> + {this.filterList().map(image => this.renderImagesListItem(image, isReadOnlyMode))} + </ListEditorView> + ); + }; + + + renderImagesListItem(image, isReadOnlyMode) { + let {id, fileName} = image; + let {onEditImageClick, version, isManual, onDeleteImage} = this.props; + return ( + <ListEditorItemView + key={id} + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditImageClick(image, version, isReadOnlyMode)} + onDelete={isManual ? () => onDeleteImage(image, version) : null}> + + <ListEditorItemViewField> + <div className='image-filename-cell'><span className='image-filename'>{fileName}</span></div> + </ListEditorItemViewField> + </ListEditorItemView> + ); + } + + filterList() { + let {imagesList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return imagesList.filter(({fileName = ''}) => { + return escape(fileName).match(filter); + }); + } + else { + return imagesList; + } + } + + save() { + let {onSubmit, qdata} = this.props; + return onSubmit({qdata}); + } +} +export default SoftwareProductComponentsImageListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js new file mode 100644 index 0000000000..20d1f5dd18 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js @@ -0,0 +1,32 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + + case actionTypes.IMAGES_LIST_UPDATE: + if (action.componentId) { + return { + ...state, + [action.componentId] : (action.response && action.response.length > 0) + }; + } + return state; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx new file mode 100644 index 0000000000..ca58b697a2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx @@ -0,0 +1,48 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; + +import Format from './Format.jsx'; +import Version from './Version.jsx'; + +const FileDetails = ({editingMode, fileName, onDataChanged, isManual, dataMap, onQDataChanged, genericFieldInfo, qgenericFieldInfo}) => { + let fileNameCols = (editingMode) ? 3 : 4; + return( + <GridSection> + <GridItem colSpan={fileNameCols}> + <Input + disabled={!isManual} + onChange={fileName => onDataChanged({fileName}, forms.IMAGE_EDIT_FORM)} + label={i18n('Image Name')} + data-test-id='image-filename' + value={fileName} + isValid={genericFieldInfo.fileName.isValid} + errorText={genericFieldInfo.fileName.errorText} + isRequired={true} + type='text' + className='image-filename'/> + </GridItem> + {editingMode && <Version isManual={isManual} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} + {editingMode && <Format isManual={isManual} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged}/>} + </GridSection> + ); +}; +export default FileDetails; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Format.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Format.jsx new file mode 100644 index 0000000000..1f71c6b277 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Format.jsx @@ -0,0 +1,47 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const Format = ({isManual, dataMap, qgenericFieldInfo, onQDataChanged}) => { + return( + <GridItem colSpan={2}> + <Input + disabled={!isManual} + data-test-id='image-format' + type='select' + label={i18n('Format')} + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['format'].isValid} + errorText={qgenericFieldInfo['format'].errorText} + value={dataMap['format']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'format' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['format'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + ); +}; +export default Format; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/ImageDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/ImageDetails.jsx new file mode 100644 index 0000000000..24e54bbbcb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/ImageDetails.jsx @@ -0,0 +1,39 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const ImageDetails = ({dataMap, qgenericFieldInfo, onQDataChanged}) => { + return( + <GridSection title={i18n('Image Details')}> + <GridItem colSpan={2}> + <Input + data-test-id='image-md5' + className='image-md5' + type='text' + label={i18n('md5')} + onChange={(md5) => onQDataChanged({'md5' : md5})} + isValid={qgenericFieldInfo['md5'].isValid} + errorText={qgenericFieldInfo['md5'].errorText} + value={dataMap['md5']}/> + </GridItem> + </GridSection> + ); +}; +export default ImageDetails; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx new file mode 100644 index 0000000000..3cac9a51b8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx @@ -0,0 +1,39 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const Version = ({isManual, dataMap, qgenericFieldInfo, onQDataChanged}) => { + return( + <GridItem colSpan={1}> + <Input + disabled={!isManual} + data-test-id='image-version' + type='text' + className='image-version' + label={i18n('Version')} + onChange={(version) => onQDataChanged({'version' : version})} + isValid={qgenericFieldInfo['version'].isValid} + errorText={qgenericFieldInfo['version'].errorText} + value={dataMap['version']}/> + </GridItem> + ); +}; +export default Version; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx index dc86771400..9ae9e359b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -14,7 +14,7 @@ * permissions and limitations under the License. */ import React from 'react'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Form from 'nfvo-components/input/validation/Form.jsx'; @@ -56,7 +56,7 @@ const TextAreaItem = ({item, toggle, expanded, genericFieldInfo, dataMap, onQDat <div className={expanded ? 'title' : 'title add-padding'} data-test-id={`btn-${item.key}`} onClick={() => toggle(item.key)}> - <SVGIcon name={expanded ? 'chevron-up' : 'chevron-down'}/> + <SVGIcon name={expanded ? 'chevronUp' : 'chevronDown'}/> <span className='title-text'>{i18n(item.description)}</span> {item.added && <div className='new-line'>{i18n(item.added)}</div>} </div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js index 293e252dca..730beba545 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js @@ -25,20 +25,19 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data:currentVSP = {}}, softwareProductComponents: {monitoring}} = softwareProduct; - let {trapFilename, pollFilename} = monitoring; + let filenames = monitoring; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { isReadOnlyMode, - trapFilename, - pollFilename + filenames }; }; const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { onDropMibFileToUpload: (formData, type) => - SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, { + SoftwareProductComponentsMonitoringAction.uploadFile(dispatch, { softwareProductId, version, componentId, @@ -46,7 +45,7 @@ const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) type }), - onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, { + onDeleteFile: type => SoftwareProductComponentsMonitoringAction.deleteFile(dispatch, { softwareProductId, version, componentId, @@ -57,7 +56,7 @@ const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) type: modalActionTypes.GLOBAL_MODAL_ERROR, data: { title: i18n('Upload Failed'), - msg: i18n('Expected "zip" file. Please check the provided file type.') + msg: i18n('Expected "zip" file. Please check the provided file type.') } }), diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js index 64403faa78..3db708bc92 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js @@ -16,23 +16,13 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -import SoftwareProductComponentsMonitoringConstants, {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; +import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; -const UPLOAD = true; - function baseUrl(vspId, version, componentId) { const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/components/${componentId}/monitors`; -} - -function snmpTrapUrl(vspId, version, componentId, isUpload) { - return `${baseUrl(vspId, version, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; -} - -function snmpPollUrl(vspId, version, componentId, isUpload) { - return `${baseUrl(vspId, version, componentId)}/snmp${isUpload ? '/upload' : ''}`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/components/${componentId}/uploads`; } let onInvalidFileSizeUpload = (dispatch) => dispatch({ @@ -43,62 +33,42 @@ let onInvalidFileSizeUpload = (dispatch) => dispatch({ } }); -let uploadSnmpTrapFile = (dispatch, {softwareProductId, version, componentId, formData}) => { - RestAPIUtil.post(snmpTrapUrl(softwareProductId, version, componentId, UPLOAD), formData).then(()=> dispatch({ - type: actionTypes.SNMP_TRAP_UPLOADED, data: {filename: formData.get('upload').name} +let uploadFile = (dispatch, {softwareProductId, version, componentId, formData, type}) => { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version, componentId)}/types/${type}`, formData).then(()=> dispatch({ + type: actionTypes.MONITOR_UPLOADED, data: {filename: formData.get('upload').name, type : type} })); }; -let uploadSnmpPollFile = (dispatch, {softwareProductId, version, componentId, formData}) => { - RestAPIUtil.post(snmpPollUrl(softwareProductId, version, componentId, UPLOAD), formData).then(()=> dispatch({ - type: actionTypes.SNMP_POLL_UPLOADED, data: {filename: formData.get('upload').name} +let deleteFile = (dispatch, {softwareProductId, version, componentId, type}) => { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/types/${type}`).then(()=> dispatch({ + type: actionTypes.MONITOR_DELETED, + data : { type: type} })); }; -let deleteSnmpTrapFile = (dispatch, {softwareProductId, version, componentId}) => { - RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ - type: actionTypes.SNMP_TRAP_DELETED - })); -}; - -let deleteSnmpPollFile = (dispatch, {softwareProductId, version, componentId}) => { - RestAPIUtil.destroy(snmpPollUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ - type: actionTypes.SNMP_POLL_DELETED - })); -}; const SoftwareProductComponentsMonitoringAction = { fetchExistingFiles(dispatch, {softwareProductId, version, componentId}){ - RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/snmp`).then(response => + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`).then(response => dispatch({ - type: actionTypes.SNMP_FILES_DATA_CHANGE, - data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll} + type: actionTypes.MONITOR_FILES_DATA_CHANGE, + data: response }) ); }, - uploadSnmpFile(dispatch, {softwareProductId, version, componentId, formData, type}){ + uploadFile(dispatch, {softwareProductId, version, componentId, formData, type}){ if (formData.get('upload').size) { - if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - uploadSnmpTrapFile(dispatch, {softwareProductId, version, componentId, formData}); - } - else { - uploadSnmpPollFile(dispatch, {softwareProductId, version, componentId, formData}); - } + return uploadFile(dispatch, {softwareProductId, version, componentId, formData, type}); } else { onInvalidFileSizeUpload(dispatch); } }, - deleteSnmpFile(dispatch, {softwareProductId, version, componentId, type}){ - if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - deleteSnmpTrapFile(dispatch, {softwareProductId, version, componentId}); - } - else { - deleteSnmpPollFile(dispatch, {softwareProductId, version, componentId}); - } + deleteFile(dispatch, {softwareProductId, version, componentId, type}){ + return deleteFile(dispatch, {softwareProductId, version, componentId, type}); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js index d908d36aaa..bf2cbd2a3f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js @@ -14,20 +14,31 @@ * permissions and limitations under the License. */ import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + export const actionTypes = keyMirror({ + MONITOR_FILES_DATA_CHANGE: null, + MONITOR_UPLOADED: null, + MONITOR_DELETED: null +}); - SNMP_FILES_DATA_CHANGE: null, +export const fileTypes = { + SNMP_TRAP: 'SNMP_TRAP', + SNMP_POLL: 'SNMP_POLL', + VES_EVENT: 'VES_EVENTS' +}; - SNMP_TRAP_UPLOADED: null, - SNMP_POLL_UPLOADED: null, +export const type2Name = { + SNMP_TRAP: 'snmpTrap', + SNMP_POLL: 'snmpPoll', + VES_EVENTS: 'vesEvent' +}; - SNMP_TRAP_DELETED: null, - SNMP_POLL_DELETED: null -}); -export default keyMirror({ - SNMP_TRAP: null, - SNMP_POLL: null -}); +export const type2Title = { + SNMP_TRAP : i18n('SNMP Trap'), + SNMP_POLL : i18n('SNMP Poll'), + VES_EVENTS: i18n('VES') +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js index 54513b9634..f5cfe6f06d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js @@ -13,35 +13,21 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ -import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; +import {actionTypes, type2Name} from './SoftwareProductComponentsMonitoringConstants.js'; export default (state = {}, action) => { switch (action.type) { - case actionTypes.SNMP_FILES_DATA_CHANGE: + case actionTypes.MONITOR_FILES_DATA_CHANGE: + return action.data; + case actionTypes.MONITOR_UPLOADED: return { ...state, - trapFilename: action.data.trapFilename, - pollFilename: action.data.pollFilename + [type2Name[action.data.type]]: action.data.filename }; - case actionTypes.SNMP_TRAP_UPLOADED: + case actionTypes.MONITOR_DELETED: 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 + [type2Name[action.data.type]]: 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 index 329cc70353..2ad48ec84b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -19,14 +19,15 @@ 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'; +import {fileTypes, type2Title, type2Name} from './SoftwareProductComponentsMonitoringConstants.js'; + + class SoftwareProductComponentsMonitoringView extends Component { static propTypes = { isReadOnlyMode: PropTypes.bool, - trapFilename: PropTypes.string, - pollFilename: PropTypes.string, + filenames: PropTypes.object, softwareProductId: PropTypes.string, onDropMibFileToUpload: PropTypes.func, @@ -38,26 +39,24 @@ class SoftwareProductComponentsMonitoringView extends Component { }; + + render() { return ( <div className='vsp-component-monitoring'> - {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_TRAP)} - {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_POLL)} + {this.renderDropzoneWithType(fileTypes.VES_EVENT)} + {this.renderDropzoneWithType(fileTypes.SNMP_TRAP)} + {this.renderDropzoneWithType(fileTypes.SNMP_POLL)} </div> ); } renderDropzoneWithType(type) { - let {isReadOnlyMode, trapFilename, pollFilename} = this.props; - let fileName; - if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - fileName = trapFilename; - } - else { - fileName = pollFilename; - } + let {isReadOnlyMode, filenames} = this.props; + let fileByType = type2Name[type]; + let fileName = (filenames) ? filenames[fileByType] : undefined; let refAndName = `fileInput${type.toString()}`; - let typeDisplayName = this.getFileTypeDisplayName(type); + let typeDisplayName = type2Title[type]; return ( <Dropzone className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`} @@ -97,7 +96,7 @@ class SoftwareProductComponentsMonitoringView extends Component { <ButtonToolbar> <ButtonGroup> <Button disabled>{filename}</Button> - <Button className='delete-button' onClick={()=>this.props.onDeleteSnmpFile(type)}>X</Button> + <Button className='delete-button' onClick={()=>this.props.onDeleteFile(type)}>X</Button> </ButtonGroup> </ButtonToolbar> ); @@ -126,11 +125,6 @@ class SoftwareProductComponentsMonitoringView extends Component { this.props.onFileUploadError(); } } - - 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/NICCreation/NICCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js new file mode 100644 index 0000000000..865367a734 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js @@ -0,0 +1,51 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import NICCreationActionHelper from './NICCreationActionHelper.js'; +import NICCreationView from './NICCreationView.jsx'; +import SoftwareProductComponentsNetworkActionHelper from '../SoftwareProductComponentsNetworkActionHelper.js'; +import {networkTypes, NIC_CREATION_FORM_NAME} from '../SoftwareProductComponentsNetworkConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents} = softwareProduct; + let {network: {nicCreation = {}}} = softwareProductComponents; + let {data, genericFieldInfo, formReady} = nicCreation; + data = {...data, networkType: networkTypes.EXTERNAL}; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + return { + currentSoftwareProduct, + data, + genericFieldInfo, + isFormValid, + formReady + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => ValidationHelper.dataChanged(dispatch, {deltaData, formName: NIC_CREATION_FORM_NAME}), + onCancel: () => NICCreationActionHelper.close(dispatch), + onSubmit: ({nic, softwareProductId, componentId, version}) => { + NICCreationActionHelper.close(dispatch); + SoftwareProductComponentsNetworkActionHelper.createNIC(dispatch, {nic, softwareProductId, componentId, version}); + }, + onValidateForm: () => ValidationHelper.validateForm(dispatch, NIC_CREATION_FORM_NAME) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(NICCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationActionHelper.js new file mode 100644 index 0000000000..ad28c86b81 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationActionHelper.js @@ -0,0 +1,47 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from '../SoftwareProductComponentsNetworkConstants'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; + +export default { + + open(dispatch, {softwareProductId, componentId, modalClassName}) { + dispatch({ + type: actionTypes.NICCreation.OPEN + }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.NIC_CREATION, + title: i18n('Create NEW NIC'), + modalClassName, + modalComponentProps: {softwareProductId, componentId} + } + }); + }, + + close(dispatch){ + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }); + dispatch({ + type: actionTypes.NICCreation.CLEAR_DATA + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationReducer.js new file mode 100644 index 0000000000..c7e2495b3d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationReducer.js @@ -0,0 +1,49 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes, NIC_CREATION_FORM_NAME} from '../SoftwareProductComponentsNetworkConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.NICCreation.OPEN: + return { + ...state, + data: {}, + formName: NIC_CREATION_FORM_NAME, + formReady: null, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data : true}] + }, + 'networkDescription' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 50}] + } + } + }; + case actionTypes.NICCreation.CLEAR_DATA: + return {}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationView.jsx new file mode 100644 index 0000000000..3cb731a421 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationView.jsx @@ -0,0 +1,123 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const NICPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + networkId: React.PropTypes.string +}); + +class NICCreationView extends React.Component { + + static propTypes = { + data: NICPropType, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {data = {}, onDataChanged, genericFieldInfo, isFormValid, onValidateForm, formReady} = this.props; + let {name, description, networkDescription} = data; + return ( + <div> + {genericFieldInfo && <Form + ref={(form) => this.form = form} + hasButtons={true} + onSubmit={ () => this.submit() } + submitButtonText={data.id ? i18n('Save') : i18n('Create')} + onReset={ () => this.props.onCancel() } + labledButtons={true} + isValid={isFormValid} + onValidateForm={() => onValidateForm()} + formReady={formReady} > + <GridSection> + <GridItem colSpan={4}> + <Input + value={name} + label={i18n('Name')} + data-test-id='nic-name' + onChange={name => onDataChanged({name})} + isRequired={true} + type='text' + isValid={genericFieldInfo['name'].isValid} + errorText={genericFieldInfo['name'].errorText} + className='field-section'/> + <Input + value={description} + label={i18n('Description')} + data-test-id='nic-description' + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo['description'].isValid} + errorText={genericFieldInfo['description'].errorText} + type='textarea' + className='field-section'/> + </GridItem> + </GridSection> + <GridSection title={i18n('Network')}> + <GridItem colSpan={2}> + <div className='form-group'> + <label className='control-label'>{i18n('Network Type')}</label> + <div className='network-type-radio'> + <Input + label={i18n('Internal')} + disabled + checked={false} + data-test-id='nic-internal' + className='network-radio disabled' + type='radio'/> + <Input + label={i18n('External')} + disabled + checked={true} + data-test-id='nic-external' + className='network-radio disabled' + type='radio'/> + </div> + </div> + </GridItem> + <GridItem colSpan={2}> + <Input + value={networkDescription} + label={i18n('Network Description')} + data-test-id='nic-network-description' + onChange={networkDescription => onDataChanged({networkDescription})} + isValid={genericFieldInfo['networkDescription'].isValid} + errorText={genericFieldInfo['networkDescription'].errorText} + type='text' + className='field-section'/> + </GridItem> + </GridSection> + </Form>} + </div> + ); + } + + + submit() { + const {data: nic, softwareProductId, componentId, currentSoftwareProduct} = this.props; + this.props.onSubmit({nic, softwareProductId, componentId, version: currentSoftwareProduct.version}); + } +} + +export default NICCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js index 7cf1f0189e..b47c7e0f99 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js @@ -20,6 +20,7 @@ import VersionControllerUtils from 'nfvo-components/panel/versionController/Vers import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct}) => { @@ -28,6 +29,7 @@ export const mapStateToProps = ({softwareProduct}) => { let {network: {nicEditor = {}}} = softwareProductComponents; let {data, qdata, genericFieldInfo, qgenericFieldInfo, dataMap, formReady} = nicEditor; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {onboardingMethod} = currentSoftwareProduct; let protocols = []; if(qdata && qdata.protocols && qdata.protocols.protocols && qdata.protocols.protocols.length){ protocols = qdata.protocols.protocols; @@ -47,7 +49,8 @@ export const mapStateToProps = ({softwareProduct}) => { genericFieldInfo, qgenericFieldInfo, isReadOnlyMode, - protocols + protocols, + isManual: onboardingMethod === onboardingMethodTypes.MANUAL }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js index b3c9fe5d98..dd37135d77 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js @@ -18,7 +18,7 @@ import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProdu export default (state = {}, action) => { switch (action.type) { - case actionTypes.NICEditor.OPEN: + case actionTypes.NICEditor.FILL_DATA: return { ...state, data: action.nic, @@ -31,12 +31,17 @@ export default (state = {}, action) => { 'name' : { isValid: true, errorText: '', + validations: [{type: 'required', data : true}] + }, + 'networkDescription' : { + isValid: true, + errorText: '', validations: [] } }, formName: forms.NIC_EDIT_FORM }; - case actionTypes.NICEditor.CLOSE: + case actionTypes.NICEditor.CLEAR_DATA: return {}; default: return state; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx index aad06c82f0..8a4c55a411 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx @@ -28,8 +28,9 @@ import NameAndPurpose from './nicEditorComponents/NameAndPurpose.jsx'; class SoftwareProductComponentsNetworkEditorView extends React.Component { render() { - let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, qgenericFieldInfo, dataMap, onDataChanged, protocols, onQDataChanged} = this.props; - let {name, description, networkName} = data; + let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, qgenericFieldInfo, + dataMap, onDataChanged, protocols, onQDataChanged, isManual, genericFieldInfo} = this.props; + let {name, description, networkName, networkType, networkDescription} = data; let netWorkValues = [{ enum: networkName, title: networkName @@ -48,10 +49,10 @@ class SoftwareProductComponentsNetworkEditorView extends React.Component { onValidateForm={() => onValidateForm() } className='vsp-components-network-editor'> <div className='editor-data'> - <NameAndPurpose name={name} description={description} onDataChanged={onDataChanged} isReadOnlyMode={isReadOnlyMode}/> + <NameAndPurpose isManual={isManual} name={name} description={description} onDataChanged={onDataChanged} isReadOnlyMode={isReadOnlyMode} genericFieldInfo={genericFieldInfo} /> <Protocols protocols={protocols} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> <IpConfig dataMap={dataMap} onQDataChanged={onQDataChanged} /> - <Network networkValues={netWorkValues} /> + <Network networkDescription={networkDescription} onDataChanged={onDataChanged} networkValues={netWorkValues} isReadOnlyMode={isReadOnlyMode} networkType={networkType} /> <Sizing qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> <InFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> <OutFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js index bc061469b1..a3cfe65128 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js @@ -15,8 +15,11 @@ */ import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; +import {actionTypes as GlobalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper as modalPagesMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; @@ -26,6 +29,14 @@ function baseUrl(softwareProductId, version, componentId) { return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/nics`; } +function createNIC({nic, vspId, componentId, version}) { + return RestAPIUtil.post(baseUrl(vspId, version, componentId), { + name: nic.name, + description: nic.description, + networkDescription: nic.networkDescription, + networkType: nic.networkType + }); +} function fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}) { return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${nicId}/questionnaire`); @@ -39,11 +50,16 @@ function fetchNICsList({softwareProductId, version, componentId}) { return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); } -function saveNIC({softwareProductId, version, componentId, nic: {id, name, description, networkId}}) { +function deleteNIC({softwareProductId, componentId, nicId, version}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/${nicId}`); +} +function saveNIC({softwareProductId, version, componentId, nic: {id, name, description, networkId, networkType, networkDescription}}) { return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${id}`,{ name, description, - networkId + networkId, + networkDescription, + networkType }); } @@ -62,23 +78,45 @@ const SoftwareProductComponentNetworkActionHelper = { }); }, - openNICEditor(dispatch, {nic = {}, data = {}}) { + openNICEditor(dispatch, {nic = {}, data = {}, softwareProductId, componentId, isReadOnlyMode, modalClassName}) { dispatch({ - type: actionTypes.NICEditor.OPEN, + type: actionTypes.NICEditor.FILL_DATA, nic: {...data, id: nic.id} }); + dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_SHOW, + data: { + modalClassName, + modalComponentProps: {softwareProductId, componentId, isReadOnlyMode}, + modalComponentName: modalPagesMapper.NIC_EDITOR, + title: i18n('Edit NIC') + } + }); }, closeNICEditor(dispatch) { dispatch({ - type: actionTypes.NICEditor.CLOSE + type: GlobalModalActions.GLOBAL_MODAL_CLOSE + }); + dispatch({ + type: actionTypes.NICEditor.CLEAR_DATA }); }, + createNIC(dispatch, {nic, softwareProductId, componentId, version}){ + return createNIC({nic, vspId: softwareProductId, componentId, version}).then(() => { + return SoftwareProductComponentNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); + }); + }, loadNICData({softwareProductId, version, componentId, nicId}) { return fetchNIC({softwareProductId, version, componentId, nicId}); }, + deleteNIC(dispatch, {softwareProductId, componentId, nicId, version}) { + return deleteNIC({softwareProductId, componentId, nicId, version}).then(() => { + return SoftwareProductComponentNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); + }); + }, loadNICQuestionnaire(dispatch, {softwareProductId, version, componentId, nicId}) { return fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}).then((response) => { ValidationHelper.qDataLoaded(dispatch, {qName: NIC_QUESTIONNAIRE ,response: { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js index 39c55d876c..8ef8fe8c18 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js @@ -20,9 +20,19 @@ export const actionTypes = keyMirror({ NIC_LIST_UPDATE: null, NICEditor: { + FILL_DATA: null, + CLEAR_DATA: null, + }, + NICCreation: { OPEN: null, - CLOSE: null - } + CLEAR_DATA: null, + DATA_CHANGED: null + }, }); +export const networkTypes = { + EXTERNAL: 'External', + INTERNAL: 'Internal' +}; export const NIC_QUESTIONNAIRE = 'nic'; +export const NIC_CREATION_FORM_NAME = 'nicCreation'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js index c2bd8ce479..0fa877e90f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js @@ -14,6 +14,7 @@ * permissions and limitations under the License. */ import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; @@ -21,16 +22,17 @@ import SoftwareProductComponentsNetworkListView from './SoftwareProductComponent import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {actionTypes as GlobalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; +import NICCreationActionHelper from './NICCreation/NICCreationActionHelper.js'; +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; - let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; - let {data} = nicEditor; + let {network: {nicList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); - let {version} = currentSoftwareProduct; - let isModalInEditMode = true; + let {version, onboardingMethod} = currentSoftwareProduct; return { version, @@ -40,9 +42,8 @@ export const mapStateToProps = ({softwareProduct}) => { qgenericFieldInfo, isValidityData, nicList, - isDisplayModal: Boolean(data), - isModalInEditMode, - isReadOnlyMode + isReadOnlyMode, + isManual: onboardingMethod === onboardingMethodTypes.MANUAL }; }; @@ -51,7 +52,16 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { return { onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), - onEditNicClick: (nic, version) => { + onAddNic: () => NICCreationActionHelper.open(dispatch, {softwareProductId, componentId, modalClassName: 'network-nic-modal-create'}), + onDeleteNic: (nic, version) => dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete "${nic.name}"?`), + onConfirmed: () => SoftwareProductComponentsNetworkActionHelper.deleteNIC(dispatch, {softwareProductId, + componentId, nicId: nic.id, version}) + } + }), + onEditNicClick: (nic, version, isReadOnlyMode) => { Promise.all([ SoftwareProductComponentsNetworkActionHelper.loadNICData({ softwareProductId, @@ -66,7 +76,8 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { nicId: nic.id }) ]).then( - ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data}) + ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data, + isReadOnlyMode, softwareProductId, componentId, modalClassName: 'network-nic-modal-edit'}) ); }, onSubmit: ({qdata, version}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx index f715016ba3..5a159b4a87 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -21,9 +21,7 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; import Input from'nfvo-components/input/validation/Input.jsx'; -import Modal from 'nfvo-components/modal/Modal.jsx'; -import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js'; class SoftwareProductComponentsNetworkView extends React.Component { @@ -32,7 +30,7 @@ class SoftwareProductComponentsNetworkView extends React.Component { }; render() { - let {dataMap, qgenericFieldInfo, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; + let {dataMap, qgenericFieldInfo, onQDataChanged, isReadOnlyMode} = this.props; return( <div className='vsp-components-network'> @@ -85,26 +83,14 @@ class SoftwareProductComponentsNetworkView extends React.Component { </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 {isReadOnlyMode} = this.props; + let {isReadOnlyMode, onAddNic, isManual} = this.props; return ( <ListEditorView title={i18n('Interfaces')} @@ -112,6 +98,8 @@ class SoftwareProductComponentsNetworkView extends React.Component { placeholder={i18n('Filter NICs by Name')} isReadOnlyMode={isReadOnlyMode} onFilter={value => this.setState({localFilter: value})} + onAdd={isManual ? onAddNic : null} + plusButtonTitle={i18n('Add NIC')} twoColumns> {this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} </ListEditorView> @@ -120,25 +108,26 @@ class SoftwareProductComponentsNetworkView extends React.Component { renderNicListItem(nic, isReadOnlyMode) { let {id, name, description, networkName = ''} = nic; - let {onEditNicClick, version} = this.props; + let {onEditNicClick, version, isManual, onDeleteNic} = this.props; return ( <ListEditorItemView key={id} isReadOnlyMode={isReadOnlyMode} - onSelect={() => onEditNicClick(nic, version)}> + onSelect={() => onEditNicClick(nic, version, isReadOnlyMode)} + onDelete={isManual ? () => onDeleteNic(nic, version) : null}> <ListEditorItemViewField> <div className='name'>{name}</div> </ListEditorItemViewField> <ListEditorItemViewField> - <div className='details'> - <div className='title'>{i18n('Purpose of NIC')}</div> - <div className='description'>{description}</div> + <div className={isManual ? 'details-col' : 'details'}> + <div className={isManual ? 'manual-title' : 'title'}>{i18n('Purpose of NIC')}</div> + <div className={isManual ? 'description' : ''}>{description ? description : i18n('N/A')}</div> </div> - <div className='details'> + {!isManual && <div className='details'> <div className='title'>{i18n('Network')}</div> <div className='artifact-name'>{networkName}</div> - </div> + </div>} </ListEditorItemViewField> </ListEditorItemView> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx index 3dc153d27f..bc692e753c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx @@ -19,7 +19,7 @@ import Input from 'nfvo-components/input/validation/Input.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -const NameAndPurpose = ({onDataChanged, isReadOnlyMode, name, description}) => { +const NameAndPurpose = ({onDataChanged, genericFieldInfo, isReadOnlyMode, name, description, isManual}) => { return ( <GridSection> @@ -28,7 +28,11 @@ const NameAndPurpose = ({onDataChanged, isReadOnlyMode, name, description}) => { label={i18n('Name')} value={name} data-test-id='nic-name' - disabled={true} + disabled={!isManual} + isRequired={true} + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo['name'].isValid} + errorText={genericFieldInfo['name'].errorText} type='text' /> </GridItem> <GridItem colSpan={2}> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx index 43afdbed3a..8d9b79e67f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx @@ -18,15 +18,17 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import Input from 'nfvo-components/input/validation/Input.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import { networkTypes } from '../SoftwareProductComponentsNetworkConstants.js'; -const Network = ({networkValues}) => { +const Network = ({networkValues, networkType, networkDescription, onDataChanged, isReadOnlyMode}) => { + const isExternal = networkType === networkTypes.EXTERNAL; return ( <GridSection title={i18n('Network')}> <GridItem> <Input label={i18n('Internal')} disabled - checked={true} + checked={!isExternal} data-test-id='nic-internal' className='network-radio disabled' type='radio'/> @@ -35,12 +37,21 @@ const Network = ({networkValues}) => { <Input label={i18n('External')} disabled - checked={false} + checked={isExternal} data-test-id='nic-external' className='network-radio disabled' type='radio'/> </GridItem> <GridItem colSpan={2}> + {isExternal ? + <Input + label={i18n('Network Description')} + value={networkDescription} + data-test-id='nic-network-description' + onChange={networkDescription => onDataChanged({networkDescription})} + disabled={isReadOnlyMode} + type='text'/> + : <Input label={i18n('Network')} data-test-id='nic-network' @@ -48,8 +59,8 @@ const Network = ({networkValues}) => { className='input-options-select' groupClassName='bootstrap-input-options' disabled={true} > - {networkValues.map(val => <option key={val.enum} value={val.enum}>{val.title}</option>)} - </Input> + {networkValues.map(val => <option key={val.enum} value={val.enum}>{val.title}</option>)} + </Input>} </GridItem> </GridSection> ); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js index a8cb709194..826201162c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js @@ -47,7 +47,7 @@ const mapActionsToProps = (dispatch, {componentId, softwareProductId}) => { onDeleteProcessClick: (process, version) => dispatch({ type: modalActionTypes.GLOBAL_MODAL_WARNING, data:{ - msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + msg: i18n(`Are you sure you want to delete "${process.name}"?`), onConfirmed: ()=> SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, {process, softwareProductId, version, componentId}) } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx index 650d6d5ebc..93d5ce886a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx @@ -44,7 +44,7 @@ class SoftwareProductProcessesView extends React.Component { return ( <div className='vsp-processes-page'> <div className='software-product-view'> - <div className='software-product-landing-view-right-side flex-column'> + <div className='software-product-landing-view-right-side vsp-components-processes-page flex-column'> {this.renderEditor()} {this.renderProcessList()} </div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js index 3b434e3ba4..a22b517fa0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -17,7 +17,7 @@ import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; -import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {actionTypes as modalActionTypes, modalSizes} from 'nfvo-components/modal/GlobalModalConstants.js'; import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import {actionTypes} from './SoftwareProductCreationConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -48,9 +48,10 @@ const SoftwareProductCreationActionHelper = { type: modalActionTypes.GLOBAL_MODAL_SHOW, data: { modalComponentName: modalContentMapper.SOFTWARE_PRODUCT_CREATION, - title: i18n('New Software Product'), + title: i18n('New Software Product'), modalComponentProps: { - vendorId + vendorId, + size: modalSizes.LARGE } } }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js index f7a738518e..a7db2b2357 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -15,6 +15,7 @@ */ import {actionTypes, SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; + export default (state = {}, action) => { switch (action.type) { case actionTypes.OPEN: @@ -50,6 +51,11 @@ export default (state = {}, action) => { isValid: true, errorText: '', validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}, {type: 'validateName', data: true}] + }, + 'onboardingMethod' : { + isValid: true, + errorText: '', + validations: [{type: 'requiredChooseOption', data: true}] } }, showModal: true diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx index 11b696855b..11f3543e39 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx @@ -18,10 +18,14 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import Validator from 'nfvo-utils/Validator.js'; import Input from 'nfvo-components/input/validation/Input.jsx'; import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + import {SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; +import {onboardingMethod as onboardingMethodConst} from '../SoftwareProductConstants.js'; const SoftwareProductPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -46,7 +50,7 @@ class SoftwareProductCreationView extends React.Component { render() { let {softwareProductCategories, data = {}, onDataChanged, onCancel, genericFieldInfo, disableVendor} = this.props; - let {name, description, vendorId, subCategory} = data; + let {name, description, vendorId, subCategory, onboardingMethod} = data; const vendorList = this.getVendorList(); return ( @@ -58,10 +62,11 @@ class SoftwareProductCreationView extends React.Component { onReset={() => onCancel() } labledButtons={true} isValid={this.props.isFormValid} + submitButtonText={i18n('Create')} formReady={this.props.formReady} onValidateForm={() => this.validate() }> - <div className='software-product-form-row'> - <div className='software-product-inline-section'> + <GridSection> + <GridItem colSpan='2'> <Input value={name} label={i18n('Name')} @@ -76,6 +81,7 @@ class SoftwareProductCreationView extends React.Component { label={i18n('Vendor')} type='select' value={vendorId} + overlayPos='bottom' isRequired={true} disabled={disableVendor} onChange={e => this.onSelectVendor(e)} @@ -108,8 +114,8 @@ class SoftwareProductCreationView extends React.Component { </optgroup>) } </Input> - </div> - <div className='software-product-inline-section'> + </GridItem> + <GridItem colSpan='2' stretch> <Input value={description} label={i18n('Description')} @@ -120,9 +126,10 @@ class SoftwareProductCreationView extends React.Component { errorText={genericFieldInfo.description.errorText} type='textarea' className='field-section' - data-test-id='new-vsp-description' /> - </div> - </div> + data-test-id='new-vsp-description'/> + </GridItem> + </GridSection> + <OnboardingProcedure genericFieldInfo={genericFieldInfo} onboardingMethod={onboardingMethod} onDataChanged={onDataChanged} /> </Form>} </div> ); @@ -174,4 +181,33 @@ class SoftwareProductCreationView extends React.Component { } } +const OnboardingProcedure = ({onboardingMethod, onDataChanged, genericFieldInfo}) => { + return( + <GridSection title={i18n('Onboarding procedure')}> + <GridItem colSpan={4}> + <Input + label={i18n('HEAT file')} + overlayPos='top' + isValid={genericFieldInfo.onboardingMethod.isValid} + checked={onboardingMethod === onboardingMethodConst.HEAT} + errorText={genericFieldInfo.onboardingMethod.errorText} + onChange={() => onDataChanged({onboardingMethod:'HEAT'},SP_CREATION_FORM_NAME)} + type='radio' + data-test-id='new-vsp-creation-procedure-heat' /> + </GridItem> + <GridItem colSpan={4}> + <Input + label={i18n('Manual')} + overlayPos='bottom' + checked={onboardingMethod === onboardingMethodConst.MANUAL} + isValid={genericFieldInfo.onboardingMethod.isValid} + errorText={genericFieldInfo.onboardingMethod.errorText} + onChange={() => onDataChanged({onboardingMethod:'Manual'},SP_CREATION_FORM_NAME)} + type='radio' + data-test-id='new-vsp-creation-procedure-manual' /> + </GridItem> + </GridSection> + ); +}; + export default SoftwareProductCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx index da975a7be2..2e0cd340de 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx @@ -60,6 +60,7 @@ export default class SoftwareProductDependenciesView extends React.Component { <div className='software-product-dependencies-title'>{i18n('Dependencies')}</div> <SelectActionTable columns={['Source', 'Relation Type', 'Target']} + numOfIcons={2} isReadOnlyMode={isReadOnlyMode} onAdd={canAdd ? onAddDependency : undefined} onAddItem={i18n('Add Rule')}> @@ -68,7 +69,8 @@ export default class SoftwareProductDependenciesView extends React.Component { key={dependency.id} onDelete={() => onDataChanged(softwareProductDependencies.filter(currentDependency => currentDependency.id !== dependency.id))} overlayMsg={i18n('There is a loop between selections')} - hasError={dependency.hasCycle}> + hasError={dependency.hasCycle} + hasErrorIndication> <SelectActionTableCell options={this.filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} selected={dependency.sourceId} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeployment.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeployment.js new file mode 100644 index 0000000000..98f773b465 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeployment.js @@ -0,0 +1,52 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import SoftwareProductDeploymentView from './SoftwareProductDeploymentView.jsx'; +import SoftwareProductDeploymentActionHelper from './SoftwareProductDeploymentActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export function mapStateToProps({softwareProduct}) { + let {softwareProductEditor: {data: currentSoftwareProduct = {}},softwareProductComponents: {componentsList}, softwareProductDeployment: {deploymentFlavors}} = softwareProduct; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + return { + isReadOnlyMode, + deploymentFlavors, + componentsList + }; +} + +function mapActionToProps(dispatch, {softwareProductId, version}) { + let modalClassName = 'deployment-flavor-editor'; + return { + onAddDeployment: componentsList => SoftwareProductDeploymentActionHelper.openDeploymentFlavorEditor(dispatch, {softwareProductId, modalClassName, componentsList, version}), + onDeleteDeployment: ({id, model}) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete "${model}"?`), + onConfirmed: () => SoftwareProductDeploymentActionHelper.deleteDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId: id, version}) + } + }), + onEditDeployment: (deploymentFlavor, componentsList) => + SoftwareProductDeploymentActionHelper.fetchDeploymentFlavor({softwareProductId, deploymentFlavorId: deploymentFlavor.id, version}).then(response => + SoftwareProductDeploymentActionHelper + .openDeploymentFlavorEditor(dispatch, {softwareProductId, componentsList, modalClassName, deploymentFlavor: {...response.data, id: response.id}, isEdit: true, version}), + ) + }; +} + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductDeploymentView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js new file mode 100644 index 0000000000..bd802b38f4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js @@ -0,0 +1,101 @@ +import {actionTypes} from './SoftwareProductDeploymentConstants.js'; +import {actionTypes as GlobalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import pickBy from 'lodash/pickBy'; + +function baseUrl(vspId, version) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/deployment-flavors`; +} + +function fetchDeploymentFlavorsList({softwareProductId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); +} + +function fetchDeploymentFlavor({softwareProductId, deploymentFlavorId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}/${deploymentFlavorId}`); +} + +function deleteDeploymentFlavor({softwareProductId, deploymentFlavorId, version}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version)}/${deploymentFlavorId}`); +} + +function createDeploymentFlavor({softwareProductId, data, version}) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, data); +} + +function editDeploymentFlavor({softwareProductId, deploymentFlavorId, data, version}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${deploymentFlavorId}`, data); +} + +const SoftwareProductDeploymentActionHelper = { + fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}) { + return fetchDeploymentFlavorsList({softwareProductId, version}).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_DEPLOYMENT_FLAVORS, + deploymentFlavors: response.results + }); + }); + }, + + fetchDeploymentFlavor({softwareProductId, deploymentFlavorId, version}) { + return fetchDeploymentFlavor({softwareProductId, deploymentFlavorId, version}); + }, + + deleteDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId, version}) { + return deleteDeploymentFlavor({softwareProductId, deploymentFlavorId, version}).then(() => { + return SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + }); + }, + + createDeploymentFlavor(dispatch, {softwareProductId, data, version}) { + return createDeploymentFlavor({softwareProductId, data, version}).then(() => { + return SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + }); + }, + + editDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId, data, version}) { + let dataWithoutId = pickBy(data, (val, key) => key !== 'id'); + return editDeploymentFlavor({softwareProductId, deploymentFlavorId, data: dataWithoutId, version}).then(() => { + return SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + }); + }, + + closeDeploymentFlavorEditor(dispatch) { + dispatch({ + type: actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_CLEAR_DATA + }); + dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_CLOSE + }); + }, + + openDeploymentFlavorEditor(dispatch, {softwareProductId, modalClassName, deploymentFlavor = {}, componentsList, isEdit = false, version}) { + let alteredDeploymentFlavor = {...deploymentFlavor}; + if (componentsList.length) { + alteredDeploymentFlavor = {...alteredDeploymentFlavor, componentComputeAssociations: deploymentFlavor.componentComputeAssociations ? + [{...deploymentFlavor.componentComputeAssociations[0], componentId: componentsList[0].id}] + : + [{componentId: componentsList[0].id, computeFlavorId: null}] + }; + } + dispatch({ + type: actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_FILL_DATA, + deploymentFlavor: alteredDeploymentFlavor + }); + dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.DEPLOYMENT_FLAVOR_EDITOR, + modalComponentProps: {softwareProductId, version}, + modalClassName, + title: isEdit ? 'Edit Deployment Flavor' : 'Create a New Deployment Flavor' + } + }); + }, +}; + +export default SoftwareProductDeploymentActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentConstants.js new file mode 100644 index 0000000000..51469b461c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentConstants.js @@ -0,0 +1,28 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + FETCH_SOFTWARE_PRODUCT_DEPLOYMENT_FLAVORS: null, + + deploymentFlavorEditor: { + DATA_CHANGED: null, + SOFTWARE_PRODUCT_DEPLOYMENT_FILL_DATA: null, + SOFTWARE_PRODUCT_DEPLOYMENT_CLEAR_DATA: null + } +}); + +export const DEPLOYMENT_FLAVORS_FORM_NAME = 'DEPLOYMENT_FLAVORS_FORM_NAME'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentListReducer.js new file mode 100644 index 0000000000..8eb91e8fcb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentListReducer.js @@ -0,0 +1,25 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductDeploymentConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_DEPLOYMENT_FLAVORS: + return [...action.deploymentFlavors]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentView.jsx new file mode 100644 index 0000000000..81477ecff7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentView.jsx @@ -0,0 +1,94 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; + +export default class SoftwareProductDeployment extends React.Component { + state = { + localFilter: '' + }; + + static propTypes = { + onAddDeployment: React.PropTypes.func.isRequired, + onDeleteDeployment: React.PropTypes.func.isRequired, + onEditDeployment: React.PropTypes.func.isRequired, + isReadOnlyMode: React.PropTypes.bool.isRequired + }; + + render() { + return ( + <div> + {this.renderList()} + </div> + ); + } + + renderList() { + let {onAddDeployment, isReadOnlyMode, componentsList} = this.props; + return ( + <ListEditorView + plusButtonTitle={i18n('Add Deployment Flavor')} + filterValue={this.state.localFilter} + placeholder={i18n('Filter Deployment')} + onAdd={() => onAddDeployment(componentsList)} + isReadOnlyMode={isReadOnlyMode} + title={i18n('Deployment Flavors')} + onFilter={value => this.setState({localFilter: value})} + twoColumns> + {this.filterList().map(deploymentFlavor => this.renderListItem(deploymentFlavor, isReadOnlyMode))} + </ListEditorView> + ); + } + + renderListItem(deploymentFlavor, isReadOnlyMode) { + let {id, model, description} = deploymentFlavor; + let {onEditDeployment, onDeleteDeployment, componentsList} = this.props; + return ( + <ListEditorItemView + key={id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditDeployment(deploymentFlavor, componentsList)} + onDelete={() => onDeleteDeployment(deploymentFlavor)}> + <ListEditorItemViewField> + <div className='model'>{model}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div className='description'>{description}</div> + </ListEditorItemViewField> + </ListEditorItemView> + ); + } + + filterList() { + let {deploymentFlavors} = this.props; + let {localFilter} = this.state; + + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return deploymentFlavors.filter(({model = '', description = ''}) => { + return escape(model).match(filter) || escape(description).match(filter); + }); + } + else { + return deploymentFlavors; + } + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js new file mode 100644 index 0000000000..6b924a2816 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js @@ -0,0 +1,88 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import SoftwareProductDeploymentEditorView from './SoftwareProductDeploymentEditorView.jsx'; +import SoftwareProdcutDeploymentActionHelper from '../SoftwareProductDeploymentActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; + +import {DEPLOYMENT_FLAVORS_FORM_NAME} from '../SoftwareProductDeploymentConstants.js'; + +export function mapStateToProps({licenseModel, softwareProduct}) { + let { + softwareProductEditor: { + data: currentSoftwareProduct = {} + }, + softwareProductComponents: { + componentsList, + computeFlavor: { + computesList + } + }, + softwareProductDeployment: { + deploymentFlavors, + deploymentFlavorEditor: { + data = {}, + genericFieldInfo, + formReady + } + } + } = softwareProduct; + + let { + featureGroup: { + featureGroupsList + } + } = licenseModel; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + let selectedFeatureGroupsIds = currentSoftwareProduct.licensingData ? currentSoftwareProduct.licensingData.featureGroups || [] : []; + let selectedFeatureGroupsList = featureGroupsList + .filter(featureGroup => selectedFeatureGroupsIds.includes(featureGroup.id)) + .map(featureGroup => ({value: featureGroup.id, label: featureGroup.name})); + + let DFNames = {}; + + deploymentFlavors.map(deployment => { + DFNames[deployment.model] = deployment.id; + }); + + return { + data, + selectedFeatureGroupsList, + genericFieldInfo, + DFNames, + isFormValid, + formReady, + isReadOnlyMode, + componentsList, + computesList, + isEdit: Boolean(data.id) + }; +} + +function mapActionsToProps(dispatch, {softwareProductId, version}) { + return { + onDataChanged: (deltaData, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: DEPLOYMENT_FLAVORS_FORM_NAME, customValidations}), + onClose: () => SoftwareProdcutDeploymentActionHelper.closeDeploymentFlavorEditor(dispatch), + onCreate: data => SoftwareProdcutDeploymentActionHelper.createDeploymentFlavor(dispatch, {softwareProductId, data, version}), + onEdit: data => SoftwareProdcutDeploymentActionHelper.editDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId: data.id, data, version}), + onValidateForm: () => ValidationHelper.validateForm(dispatch, DEPLOYMENT_FLAVORS_FORM_NAME) + }; +} + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductDeploymentEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorReducer.js new file mode 100644 index 0000000000..70836e8ff9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorReducer.js @@ -0,0 +1,44 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes, DEPLOYMENT_FLAVORS_FORM_NAME} from '../SoftwareProductDeploymentConstants.js';; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_FILL_DATA: + return { + ...state, + data: action.deploymentFlavor, + formReady: null, + formName: DEPLOYMENT_FLAVORS_FORM_NAME, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 500}] + }, + 'model' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + } + }; + case actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_CLEAR_DATA: + return {}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorView.jsx new file mode 100644 index 0000000000..2d621cd2f5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorView.jsx @@ -0,0 +1,137 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import SelectInput from 'nfvo-components/input/SelectInput.jsx'; +import SelectActionTable from 'nfvo-components/table/SelectActionTable.jsx'; +import SelectActionTableRow from 'nfvo-components/table/SelectActionTableRow.jsx'; +import SelectActionTableCell from 'nfvo-components/table/SelectActionTableCell.jsx'; +import Validator from 'nfvo-utils/Validator.js'; + +export default class SoftwareProductDeploymentEditorView extends React.Component { + render() { + let {data, isEdit, onClose, onDataChanged, isReadOnlyMode, selectedFeatureGroupsList, componentsList, computesList, genericFieldInfo} = this.props; + let {model, description, featureGroupId, componentComputeAssociations = []} = data; + let featureGroupsExist = selectedFeatureGroupsList.length > 0; + return ( + <div> + {genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + onSubmit={ () => this.submit() } + submitButtonText={isEdit ? i18n('Save') : i18n('Create')} + onReset={ () => onClose() } + onValidateForm={() => this.validate() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + className='vsp-deployment-editor'> + <GridSection> + <GridItem colSpan={1}> + <Input + onChange={model => onDataChanged({model}, {model: model => this.validateName(model)})} + label={i18n('Model')} + value={model} + data-test-id='deployment-model' + isValid={genericFieldInfo.model.isValid} + errorText={genericFieldInfo.model.errorText} + isRequired={true} + type='text'/> + </GridItem> + <GridItem colSpan={3}> + <Input + onChange={description => onDataChanged({description})} + label={i18n('Description')} + value={description} + data-test-id='deployment-description' + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + type='text'/> + </GridItem> + </GridSection> + <GridSection className={`deployment-feature-groups-section${!featureGroupsExist ? ' no-feature-groups' : ''}`} title={i18n('License Details')}> + <GridItem colSpan={1}> + <SelectInput + data-test-id='deployment-feature-groups' + label={i18n('Feature Group')} + value={featureGroupId} + onChange={featureGroup => onDataChanged({featureGroupId: featureGroup ? featureGroup.value : null})} + type='select' + clearable={true} + disabled={isReadOnlyMode || !featureGroupsExist} + className='field-section' + options={selectedFeatureGroupsList}/> + </GridItem> + </GridSection> + {!featureGroupsExist && <GridSection className='deployment-feature-group-warning-section'> + <GridItem colSpan={3}> + <span>{i18n('Please assign Feature Groups in VSP General')}</span> + </GridItem> + </GridSection>} + <GridSection title={i18n('Assign VFCs and Compute Flavors')} className='vfc-table'> + <GridItem colSpan={4}> + <SelectActionTable + columns={['Virtual Function Components', 'Compute Flavors']} + numOfIcons={0}> + {componentComputeAssociations.map( (association, index) => + <SelectActionTableRow key={association.componentId}> + <SelectActionTableCell + options={ + componentsList + .map(component => ({value: component.id, label: component.displayName}) ) + } + selected={association.componentId} + onChange={componentId => { + let newAssociations = [...componentComputeAssociations]; + newAssociations[index] = {...newAssociations[index], componentId}; + onDataChanged({componentComputeAssociations: newAssociations}); + }} + disabled={true}/> + <SelectActionTableCell + options={ + computesList + .filter(compute => compute.componentId === association.componentId) + .map(compute => ({value: compute.computeFlavorId, label: compute.name}) ) + } + selected={association.computeFlavorId} + onChange={computeFlavorId => { + let newAssociations = [...componentComputeAssociations]; + newAssociations[index] = {...newAssociations[index], computeFlavorId}; + onDataChanged({componentComputeAssociations: newAssociations}); + }} + disabled={isReadOnlyMode}/> + </SelectActionTableRow> + )} + </SelectActionTable> + </GridItem> + </GridSection> + </Form>} + </div> + ); + } + + validateName(value) { + const {data: {id = ''}, DFNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: DFNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Deployment flavor by the name \'' + value + '\' already exists. Deployment flavor name must be unique')}; + } + + submit(){ + let {isEdit, onCreate, onEdit, onClose, data} = this.props; + if (isEdit) { + onEdit(data); + } else { + onCreate(data); + } + onClose(); + } + + validate() { + this.props.onValidateForm(); + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx index 1d52da38b0..98dd7730bd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx @@ -65,7 +65,7 @@ class GeneralSection extends React.Component { isRequired={true} errorText={genericFieldInfo.name.errorText} isValid={genericFieldInfo.name.isValid} - onChange={name => this.props.onDataChanged({name}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS)}/> + onChange={name => name.length <= 25 && this.props.onDataChanged({name}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS)}/> <Input data-test-id='vsp-vendor-name' label={i18n('Vendor')} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js index e8091bf8d1..8806ffd0bf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js @@ -20,6 +20,7 @@ import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; import LandingPageView from './SoftwareProductLandingPageView.jsx'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {onboardingMethod} from '../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct; @@ -52,7 +53,8 @@ export const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreemen fullCategoryDisplayName }, isReadOnlyMode, - componentsList + componentsList, + isManual: currentSoftwareProduct.onboardingMethod === onboardingMethod.MANUAL }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx index 5fbf1b74b0..d3738e3ea4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -19,11 +19,10 @@ import Dropzone from 'react-dropzone'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; -import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; -import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; + +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; +import SoftwareProductComponentsList from '../components/SoftwareProductComponentsList.js'; const SoftwareProductPropType = React.PropTypes.shape({ name: React.PropTypes.string, @@ -47,7 +46,7 @@ const ComponentPropType = React.PropTypes.shape({ class SoftwareProductLandingPageView extends React.Component { state = { - localFilter: '', + fileName: '', dragging: false, files: [] @@ -67,13 +66,13 @@ class SoftwareProductLandingPageView extends React.Component { }; render() { - let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props; + let {currentSoftwareProduct, isReadOnlyMode, isManual, onDetailsSelect, 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)} + onDrop={files => this.handleImportSubmit(files, isReadOnlyMode, isManual)} + onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode, isManual)} onDragLeave={() => this.setState({dragging:false})} multiple={false} disableClick={true} @@ -84,68 +83,29 @@ class SoftwareProductLandingPageView extends React.Component { <div className='draggable-wrapper'> <div className='software-product-landing-view-top'> <div className='row'> - {this.renderProductSummary(currentSoftwareProduct)} - {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)} + <ProductSummary currentSoftwareProduct={currentSoftwareProduct} onDetailsSelect={onDetailsSelect} /> + {isManual ? + <div className='details-panel'/> + : this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)} </div> </div> </div> </Dropzone> - { - componentsList.length > 0 && this.renderComponents() - } + <SoftwareProductComponentsList + isReadOnlyMode={isReadOnlyMode} + componentsList={componentsList} + isManual={isManual} + currentSoftwareProduct={currentSoftwareProduct}/> </div> ); } - handleOnDragEnter(isReadOnlyMode) { - if (!isReadOnlyMode) { + handleOnDragEnter(isReadOnlyMode, isManual) { + if (!isReadOnlyMode && !isManual) { 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 className='single-detail-section title-text'> - {name} - </div> - </div> - <div className='details-section'> - <div className='multiple-details-section'> - <div className='detail-col' > - <div className='title'>{i18n('Vendor')}</div> - <div className='description'>{vendorName}</div> - </div> - <div className='detail-col'> - <div className='title'>{i18n('Category')}</div> - <div className='description'>{fullCategoryDisplayName}</div> - </div> - <div className='detail-col'> - <div className='title extra-large'>{i18n('License Agreement')}</div> - <div className='description'> - {this.renderLicenseAgreement(licenseAgreementName)} - </div> - </div> - </div> - <div className='single-detail-section'> - <div className='title'>{i18n('Description')}</div> - <div className='description'>{description}</div> - </div> - </div> - </div> - </div> - </div> - ); - } - renderProductDetails(currentSoftwareProduct, isReadOnlyMode) { let {validationData} = currentSoftwareProduct; let {onAttachmentsSelect} = this.props; @@ -181,64 +141,8 @@ class SoftwareProductLandingPageView extends React.Component { ); } - renderComponents() { - const {localFilter} = this.state; - - return ( - <ListEditorView - title={i18n('Virtual Function Components')} - filterValue={localFilter} - placeholder={i18n('Filter Components')} - onFilter={value => this.setState({localFilter: value})} - twoColumns> - {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})}> - <ListEditorItemViewField> - <div className='name'>{displayName}</div> - </ListEditorItemViewField> - <ListEditorItemViewField> - <div className='description'>{description}</div> - </ListEditorItemViewField> - </ListEditorItemView> - ); - } - - renderLicenseAgreement(licenseAgreementName) { - if (licenseAgreementName !== null && !licenseAgreementName) { - return (<div className='missing-license'><SVGIcon name='exclamation-triangle-full'/><div className='warning-text'>{i18n('Missing')}</div></div>); - } - 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) { + handleImportSubmit(files, isReadOnlyMode, isManual) { + if (isReadOnlyMode || isManual) { return; } if (files[0] && files[0].size) { @@ -280,4 +184,54 @@ class SoftwareProductLandingPageView extends React.Component { } } +const ProductSummary = ({currentSoftwareProduct, onDetailsSelect}) => { + let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct; + 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 className='single-detail-section title-text'> + {name} + </div> + </div> + <div className='details-section'> + <div className='multiple-details-section'> + <div className='detail-col' > + <div className='title'>{i18n('Vendor')}</div> + <div className='description'>{vendorName}</div> + </div> + <div className='detail-col'> + <div className='title'>{i18n('Category')}</div> + <div className='description'>{fullCategoryDisplayName}</div> + </div> + <div className='detail-col'> + <div className='title extra-large'>{i18n('License Agreement')}</div> + <div className='description'> + <LicenseAgreement licenseAgreementName={licenseAgreementName}/> + </div> + </div> + </div> + <div className='single-detail-section'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> + </div> + </div> + </div> + </div> + ); +}; + + +const LicenseAgreement = ({licenseAgreementName}) => { + if (!licenseAgreementName) { + return (<div className='missing-license'><SVGIcon name='exclamationTriangleFull'/><div className='warning-text'>{i18n('Missing')}</div></div>); + } + return <div>{licenseAgreementName}</div>; +}; + export default SoftwareProductLandingPageView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js index 66926ce4b3..afd6331324 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js @@ -42,7 +42,7 @@ const mapActionsToProps = (dispatch, {softwareProductId}) => { onDeleteProcess: (process, version) => dispatch({ type: modalActionTypes.GLOBAL_MODAL_WARNING, data:{ - msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + msg: i18n(`Are you sure you want to delete "${process.name}"?`), onConfirmed: ()=> SoftwareProductProcessesActionHelper.deleteProcess(dispatch, {process, softwareProductId, version}) } |