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