diff options
author | Avi Ziv <avi.ziv@amdocs.com> | 2017-07-18 19:45:38 +0300 |
---|---|---|
committer | Avi Ziv <avi.ziv@amdocs.com> | 2017-07-18 19:45:38 +0300 |
commit | b8e2faf476202b6ffe61bc3a9a37df1304881d40 (patch) | |
tree | f78b8c0517d8e16c5ae610bf8b49f68ea8a312a1 /openecomp-ui/src | |
parent | 75aacbbe1acf78fa53378f07f0a8c7769449a17e (diff) |
[SDC] Onboarding 1710 rebase.
Change-Id: If3b6b81d221fde13908f1e8160db6f7d9433c535
Signed-off-by: Avi Ziv <avi.ziv@amdocs.com>
Diffstat (limited to 'openecomp-ui/src')
128 files changed, 4031 insertions, 869 deletions
diff --git a/openecomp-ui/src/index.html b/openecomp-ui/src/index.html index 7f4fe04f06..d4fab4dcb2 100644 --- a/openecomp-ui/src/index.html +++ b/openecomp-ui/src/index.html @@ -9,34 +9,6 @@ <body> <div id="sdc-app" class="sdc-app"></div> -<script> - -(function(){ - - /** - * Bundle Import script( By Language)! - */ - - var DEFAULT_LANG = 'en'; - var lang = localStorage.getItem('user_locale') || ((navigator && (navigator.language || navigator.userLanguage)) || DEFAULT_LANG).toLowerCase(); - - function writeAppBundle() { - var supportedLangs = [ - //<!--prod:supported-langs--><!--/prod:supported-langs--> - ]; - if(-1 === supportedLangs.indexOf(lang)) { - lang = DEFAULT_LANG; - } - - var bundleScript = document.createElement('script'); - bundleScript.src = 'bundle_' + lang + '.js'; - document.write(bundleScript.outerHTML); - } - - writeAppBundle(); - -})() -</script> - </body> </html> +npm diff --git a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx index 0759f2c28d..5fe592a663 100644 --- a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx +++ b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx @@ -16,7 +16,7 @@ import React, {Component} from 'react'; import ListGroupItem from 'react-bootstrap/lib/ListGroupItem.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import Icon from 'nfvo-components/icon/Icon.jsx'; import {Collapse} from 'react-bootstrap'; /** @@ -145,7 +145,7 @@ class ErrorBlock extends React.Component { const ErrorHeader = ({errorType, collapsed, onClick}) => { return( <div onClick={onClick} className='error-block-header'> - <SVGIcon iconClassName={collapsed ? '' : 'right' } name='chevron-down'/> + <SVGIcon iconClassName={collapsed ? '' : 'right' } name='chevronDown'/> {errorType} </div> ); diff --git a/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx b/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx new file mode 100644 index 0000000000..cd39376bda --- /dev/null +++ b/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx @@ -0,0 +1,75 @@ +import React from 'react'; +import DatePicker from 'react-datepicker'; +import moment from 'moment'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; + +class CustomInput extends React.Component { + + static propTypes = { + placeHolderText: React.PropTypes.string, + onChange: React.PropTypes.func, + onClick: React.PropTypes.func, + value: React.PropTypes.string + }; + + render() { + const {placeholderText, onClick, onClear, inputRef, value: date} = this.props; + const text = date ? date : placeholderText; + const textStyle = date ? '' : 'placeholder'; + return ( + <div ref={inputRef} className='datepicker-custom-input'> + <div onClick={onClick} className={`datepicker-text ${textStyle}`}>{text}</div> + <div onClick={onClear} className='clear-input'/> + <SVGIcon onClick={onClick} name='calendar'/> + </div> + ); + } +}; + +const parseDate = (date, format) => { + return typeof date === 'number' ? moment.unix(date) : moment(date, format); +}; + +class Datepicker extends React.Component { + static propTypes = { + date: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]), + format: React.PropTypes.string, + onChange: React.PropTypes.func, + selectsStart: React.PropTypes.bool, + selectsEnd: React.PropTypes.bool, + startDate: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]), + endDate: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]), + disabled: React.PropTypes.bool, + label: React.PropTypes.string, + isRequired: React.PropTypes.bool + } + render() { + let {date, format, onChange, selectsStart = false, startDate = null, endDate = null, selectsEnd = false, + disabled = false, inputRef} = this.props; + const placeholderText = 'Enter a date'; + const props = { + format, + onChange, + disabled, + selected: date ? parseDate(date, format) : date, + selectsStart, + selectsEnd, + placeholderText, + startDate: startDate ? parseDate(startDate, format) : startDate, + endDate: endDate ? parseDate(endDate, format) : endDate + }; + + return ( + <div className='customized-date-picker'> + <DatePicker + calendarClassName='customized-date-picker-calendar' + customInput={<CustomInput inputRef={inputRef} onClear={() => onChange(undefined)} placeholderText={placeholderText}/>} + minDate={selectsEnd && props.startDate} + maxDate={selectsStart && props.endDate} + {...props}/> + </div> + ); + } +} + +export default Datepicker; diff --git a/openecomp-ui/src/nfvo-components/grid/GridSection.jsx b/openecomp-ui/src/nfvo-components/grid/GridSection.jsx index 175b3ee082..de8a4f3e64 100644 --- a/openecomp-ui/src/nfvo-components/grid/GridSection.jsx +++ b/openecomp-ui/src/nfvo-components/grid/GridSection.jsx @@ -14,10 +14,11 @@ * permissions and limitations under the License. */ import React from 'react'; +import classnames from 'classnames'; -const GridSection = ({title, children, titleClassName}) => { +const GridSection = ({title, children, className, titleClassName}) => { return ( - <div className='grid-section'> + <div className={classnames('grid-section', className)}> {title && <div className={`section-title ${titleClassName || ''}`}>{title}</div>} <div className='grid-items'> {children} diff --git a/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx b/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx deleted file mode 100644 index dd165fb52c..0000000000 --- a/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx +++ /dev/null @@ -1,54 +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, {PropTypes} from 'react'; -import Configuration from 'sdc-app/config/Configuration.js'; - -export default class SVGIcon extends React.Component { - - static propTypes = { - name: PropTypes.string.isRequired, - onClick: PropTypes.func, - label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - labelPosition: PropTypes.string, - className: PropTypes.string, - iconClassName: PropTypes.string, - labelClassName: PropTypes.string - }; - - static defaultProps = { - name: '', - label: '', - className: '', - iconClassName: '', - labelClassName: '', - labelPosition: 'bottom' - }; - - render() { - let {name, onClick, label, className, iconClassName, labelClassName, labelPosition, ...other} = this.props; - let classes = `svg-icon-wrapper ${className} ${onClick ? 'clickable' : ''} ${labelPosition}`; - - return ( - <div {...other} onClick={onClick} className={classes}> - <svg className={`svg-icon ${name} ${iconClassName}`} > - <use href={Configuration.get('appContextPath') + '/resources/images/svg/' + this.props.name + '.svg#' + this.props.name + '_icon' } - xlinkHref={Configuration.get('appContextPath') + '/resources/images/svg/' + this.props.name + '.svg#' + this.props.name + '_icon' } /> - </svg> - {label && <span className={`svg-icon-label ${labelClassName}`}>{label}</span>} - </div> - ); - } -} diff --git a/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js b/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js deleted file mode 100644 index 6675670cea..0000000000 --- a/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import {storiesOf, action} from '@kadira/storybook'; -import {select, text, withKnobs} from '@kadira/storybook-addon-knobs'; -import SVGIcon from './SVGIcon.jsx'; - -const stories = storiesOf('SVGIcon', module); - -const iconNames = ['locked', - 'pencil', - 'plus-circle', - 'plus', - 'search', - 'sliders', - 'trash-o', - 'unlocked', - 'vendor', - 'version-controller-lock-closed', - 'version-controller-lock-open', - 'version-controller-revert', - 'version-controller-save', - 'version-controller-submit', - 'vlm', - 'vsp' ]; - -function colorChanger() { - return {fill: text('Color', '')}; -} - -function iconName() { - return select('Icon name' , iconNames, iconNames[0]); -} - -stories.addDecorator(withKnobs); - -stories - .add('icon', () => { - return ( - <SVGIcon name={iconName()} style={colorChanger()}/> - ); - }) - .add('icon with label', () => { - return ( - <SVGIcon name={iconName()} label={iconName()} style={colorChanger()}/> - ); - }) - .add('locked clickable', () => { - return ( - <SVGIcon name={iconName()} onClick={action('clicked')} style={colorChanger()}/> - ); - });
\ No newline at end of file diff --git a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx index e2ee40fcd2..eab1d45ef4 100644 --- a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx @@ -15,11 +15,11 @@ */ import React from 'react'; import ReactDOM from 'react-dom'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; const ExpandableInputClosed = ({iconType, onClick}) => ( - <SVGIcon className='expandable-input-wrapper closed' name={iconType} onClick={onClick} /> + <SVGIcon className='expandable-input-wrapper closed' data-test-id='expandable-input-closed' name={iconType} onClick={onClick} /> ); class ExpandableInputOpened extends React.Component { @@ -58,6 +58,7 @@ class ExpandableInputOpened extends React.Component { <div className='expandable-input-wrapper opened' key='expandable'> <Input type='text' + data-test-id='expandable-input-opened' value={value} ref={(input) => this.searchInputNode = input} className='expandable-active' @@ -65,7 +66,7 @@ class ExpandableInputOpened extends React.Component { onChange={e => onChange(e)} onKeyDown={e => this.handleKeyDown(e)} onBlur={handleBlur}/> - {value && <SVGIcon onClick={() => this.handleClose()} name='close' />} + {value && <SVGIcon data-test-id='expandable-input-close-btn' onClick={() => this.handleClose()} name='close' />} {!value && <SVGIcon name={iconType} onClick={handleBlur}/>} </div> ); diff --git a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx index c60d6f777e..a3be363ba4 100644 --- a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx +++ b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.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 Input from 'nfvo-components/input/validation/InputWrapper.jsx'; class DualListboxView extends React.Component { @@ -118,10 +118,10 @@ class DualListboxView extends React.Component { renderOperationsBar(isReadOnlyMode) { return ( <div className={`dual-list-options-bar${isReadOnlyMode ? ' disabled' : ''}`}> - {this.renderOperationBarButton(() => this.addToSelectedList(), 'angle-right')} - {this.renderOperationBarButton(() => this.removeFromSelectedList(), 'angle-left')} - {this.renderOperationBarButton(() => this.addAllToSelectedList(), 'angle-double-right')} - {this.renderOperationBarButton(() => this.removeAllFromSelectedList(), 'angle-double-left')} + {this.renderOperationBarButton(() => this.addToSelectedList(), 'angleRight')} + {this.renderOperationBarButton(() => this.removeFromSelectedList(), 'angleLeft')} + {this.renderOperationBarButton(() => this.addAllToSelectedList(), 'angleDoubleRight')} + {this.renderOperationBarButton(() => this.removeAllFromSelectedList(), 'angleDoubleLeft')} </div> ); } diff --git a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx index 98810d1c0d..8d53322587 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx @@ -25,7 +25,9 @@ class Form extends React.Component { onReset : null, labledButtons: true, onValidChange : null, - isValid: true + isValid: true, + submitButtonText: null, + cancelButtonText: null }; static propTypes = { @@ -36,6 +38,8 @@ class Form extends React.Component { onSubmit : React.PropTypes.func, onReset : React.PropTypes.func, labledButtons: React.PropTypes.bool, + submitButtonText: React.PropTypes.string, + cancelButtonText: React.PropTypes.string, onValidChange : React.PropTypes.func, onValidityChanged: React.PropTypes.func, onValidateForm: React.PropTypes.func @@ -48,7 +52,8 @@ class Form extends React.Component { render() { // eslint-disable-next-line no-unused-vars - let {isValid, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, onDataChanged, children, ...formProps} = this.props; + let {isValid, onValidChange, onValidityChanged, onDataChanged, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, submitButtonText, + cancelButtonText, children, ...formProps} = this.props; return ( <form {...formProps} ref={(form) => this.form = form} onSubmit={event => this.handleFormValidation(event)}> <div className='validation-form-content'> @@ -56,7 +61,13 @@ class Form extends React.Component { {children} </fieldset> </div> - {hasButtons && <ValidationButtons labledButtons={labledButtons} ref={(buttons) => this.buttons = buttons} isReadOnlyMode={isReadOnlyMode}/>} + {hasButtons && + <ValidationButtons + labledButtons={labledButtons} + submitButtonText={submitButtonText} + cancelButtonText={cancelButtonText} + ref={(buttons) => this.buttons = buttons} + isReadOnlyMode={isReadOnlyMode}/>} </form> ); } diff --git a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx index 59c35d7993..eef8fee1ce 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx @@ -22,6 +22,7 @@ import FormGroup from 'react-bootstrap/lib/FormGroup.js'; import FormControl from 'react-bootstrap/lib/FormControl.js'; import Overlay from 'react-bootstrap/lib/Overlay.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import Datepicker from 'nfvo-components/datepicker/Datepicker.jsx'; class Input extends React.Component { @@ -29,13 +30,14 @@ class Input extends React.Component { value: this.props.value, checked: this.props.checked, selectedValues: [] - } + }; render() { const {label, isReadOnlyMode, value, onBlur, onKeyDown, type, disabled, checked, name} = this.props; // eslint-disable-next-line no-unused-vars - const {groupClassName, isValid = true, errorText, isRequired, ...inputProps} = this.props; - let wrapperClassName = (type !== 'radio') ? 'validation-input-wrapper' : 'form-group'; + const {groupClassName, isValid = true, errorText, isRequired, overlayPos, ...inputProps} = this.props; + const {dateFormat, startDate, endDate, selectsStart, selectsEnd} = this.props; // Date Props + let wrapperClassName = (type !== 'radio') ? 'validation-input-wrapper' : 'validation-radio-wrapper'; if (disabled) { wrapperClassName += ' disabled'; } @@ -43,7 +45,7 @@ class Input extends React.Component { <div className={wrapperClassName}> <FormGroup className={classNames('form-group', [groupClassName], {'required' : isRequired , 'has-error' : !isValid})} > {(label && (type !== 'checkbox' && type !== 'radio')) && <label className='control-label'>{label}</label>} - {(type === 'text' || type === 'number') && + {type === 'text' && <FormControl bsClass={'form-control input-options-other'} onChange={(e) => this.onChange(e)} @@ -54,6 +56,17 @@ class Input extends React.Component { inputRef={(input) => this.input = input} type={type} data-test-id={this.props['data-test-id']}/>} + {type === 'number' && + <FormControl + bsClass={'form-control input-options-other'} + onChange={(e) => this.onChange(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + onBlur={onBlur} + onKeyDown={onKeyDown} + value={(value !== undefined) ? value : ''} + inputRef={(input) => this.input = input} + type={type} + data-test-id={this.props['data-test-id']}/>} {type === 'textarea' && <FormControl @@ -81,6 +94,7 @@ class Input extends React.Component { disabled={isReadOnlyMode || Boolean(disabled)} value={value} onChange={(e)=>this.onChangeRadio(e)} + inputRef={(input) => this.input = input} data-test-id={this.props['data-test-id']}>{label}</Radio>} {type === 'select' && <FormControl onClick={ (e) => this.optionSelect(e) } @@ -88,6 +102,18 @@ class Input extends React.Component { inputRef={(input) => this.input = input} name={name} {...inputProps} data-test-id={this.props['data-test-id']}/>} + {type === 'date' && + <Datepicker + date={value} + format={dateFormat} + startDate={startDate} + endDate={endDate} + inputRef={(input) => this.input = input} + onChange={this.props.onChange} + disabled={isReadOnlyMode || Boolean(disabled)} + data-test-id={this.props['data-test-id']} + selectsStart={selectsStart} + selectsEnd={selectsEnd} />} </FormGroup> { this.renderErrorOverlay() } </div> @@ -116,7 +142,11 @@ class Input extends React.Component { const {onChange, type} = this.props; let value = e.target.value; if (type === 'number') { - value = Number(value); + if (value === '') { + value = undefined; + } else { + value = Number(value); + } } this.setState({ value @@ -154,7 +184,9 @@ class Input extends React.Component { else if (type === 'text' || type === 'email' || type === 'number' - || type === 'password') { + || type === 'radio' + || type === 'password' + || type === 'date') { position = 'bottom'; } diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx index 5ca716cc20..6c8115deee 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx @@ -71,6 +71,7 @@ class InputWrapper extends React.Component { checked={checked} disabled={isReadOnlyMode || Boolean(disabled)} value={value} + ref={(input) => this.inputWrapper = input} onChange={(e)=>this.onChangeRadio(e)} data-test-id={this.props['data-test-id']}>{label}</Radio>} {type === 'select' && diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx index ebb1473c04..c3808dd2c3 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx @@ -22,14 +22,16 @@ */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import Button from 'react-bootstrap/lib/Button.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Button from 'sdc-ui/lib/react/Button.js'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; class ValidationButtons extends React.Component { static propTypes = { labledButtons: React.PropTypes.bool.isRequired, - isReadOnlyMode: React.PropTypes.bool + isReadOnlyMode: React.PropTypes.bool, + submitButtonText: React.PropTypes.string, + cancelButtonText: React.PropTypes.string }; state = { @@ -37,16 +39,16 @@ class ValidationButtons extends React.Component { }; render() { - var submitBtn = this.props.labledButtons ? i18n('Save') : <SVGIcon className='check' name='check'/>; - var closeBtn = this.props.labledButtons ? i18n('Cancel') : <SVGIcon className='close' name='close'/>; + let submitBtn = this.props.labledButtons ? this.props.submitButtonText ? this.props.submitButtonText : i18n('Save') : <SVGIcon className='check' name='check'/>; + let closeBtn = this.props.labledButtons ? this.props.cancelButtonText ? this.props.cancelButtonText : i18n('Cancel') : <SVGIcon className='close' name='close'/>; return ( <div className='validation-buttons'> {!this.props.isReadOnlyMode ? <div> - <Button bsStyle='primary' ref='submitbutton' type='submit' disabled={!this.state.isValid}>{submitBtn}</Button> - <Button type='reset'>{closeBtn}</Button> + <Button type='submit' disabled={!this.state.isValid}>{submitBtn}</Button> + <Button btnType='outline' type='reset'>{closeBtn}</Button> </div> - : <Button type='reset'>{i18n('Close')}</Button> + : <Button btnType='outline' type='reset'>{i18n('Close')}</Button> } </div> ); diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx index f6c906b56b..8d7c63f567 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx @@ -16,18 +16,18 @@ import React from 'react'; import classnames from 'classnames'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import store from 'sdc-app/AppStore.js'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; class ListEditorItem extends React.Component { static propTypes = { - onSelect: React.PropTypes.func, - onDelete: React.PropTypes.func, - onEdit: React.PropTypes.func, + onSelect: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.bool]), + onDelete: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.bool]), + onEdit: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.bool]), children: React.PropTypes.node, isReadOnlyMode: React.PropTypes.bool - } + }; render() { let {onDelete, onSelect, onEdit, children, isReadOnlyMode} = this.props; @@ -39,7 +39,7 @@ class ListEditorItem extends React.Component { </div> {(onEdit || onDelete) && <div className='list-editor-item-view-controller'> {onEdit && <SVGIcon name='sliders' onClick={() => this.onClickedItem(onEdit)}/>} - {onDelete && isAbilityToDelete && <SVGIcon name='trash-o' onClick={() => this.onClickedItem(onDelete)}/>} + {onDelete && isAbilityToDelete && <SVGIcon name='trashO' onClick={() => this.onClickedItem(onDelete)}/>} </div>} </div> ); @@ -52,8 +52,8 @@ class ListEditorItem extends React.Component { store.dispatch({ type: modalActionTypes.GLOBAL_MODAL_WARNING, data: { - title: i18n('Error'), - msg: i18n('This item is checkedin/submitted, Click Check Out to continue') + title: i18n('Error'), + msg: i18n('This item is checkedin/submitted, Click Check Out to continue') } }); } diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModal.js b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js index 65a1ad683b..825cc609a8 100644 --- a/openecomp-ui/src/nfvo-components/modal/GlobalModal.js +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js @@ -18,31 +18,33 @@ import React from 'react'; import {connect} from 'react-redux'; import Modal from 'nfvo-components/modal/Modal.jsx'; -import Button from 'react-bootstrap/lib/Button.js'; +import Button from 'sdc-ui/lib/react/Button.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import {modalContentComponents} from 'sdc-app/common/modal/ModalContentMapper.js'; import {actionTypes, typeEnum} from './GlobalModalConstants.js'; const typeClass = { - 'default': 'primary', - error: 'danger', + 'default': 'default', + error: 'negative', warning: 'warning', - success: 'success' + success: 'positive' }; -const ModalFooter = ({type, onConfirmed, onDeclined, onClose, confirmationButtonText, cancelButtonText}) => +const ModalFooter = ({type, onConfirmed, onDeclined, onClose, confirmationButtonText, cancelButtonText}) => <Modal.Footer> - <Button bsStyle={typeClass[type]} onClick={onDeclined ? () => { - onDeclined(); - onClose();} : () => onClose()}> - {cancelButtonText} + <div className='sdc-modal-footer'> + {onConfirmed && <Button color={typeClass[type]} onClick={() => { + onConfirmed(); + onClose(); + }}>{confirmationButtonText}</Button>} + <Button btnType='outline' color={typeClass[type]} onClick={onDeclined ? () => { + onDeclined(); + onClose();} : () => onClose()}> + {cancelButtonText} </Button> - {onConfirmed && <Button bsStyle={typeClass[type]} onClick={() => { - onConfirmed(); - onClose(); - }}>{confirmationButtonText}</Button>} + </div> </Modal.Footer>; ModalFooter.defaultProps = { @@ -87,7 +89,7 @@ export class GlobalModalView extends React.Component { }; render() { - let {title, type, show, modalComponentName, modalComponentProps, + let {title, type, show, modalComponentName, modalComponentProps, modalClassName, msg, onConfirmed, onDeclined, confirmationButtonText, cancelButtonText, onClose} = this.props; const ComponentToRender = modalContentComponents[modalComponentName]; return ( @@ -96,7 +98,7 @@ export class GlobalModalView extends React.Component { <Modal.Title>{title}</Modal.Title> </Modal.Header> <Modal.Body> - {ComponentToRender ? <ComponentToRender {...modalComponentProps}/> : msg} + {ComponentToRender ? <ComponentToRender {...modalComponentProps}/> : msg} </Modal.Body> {(onConfirmed || onDeclined || type !== typeEnum.DEFAULT) && <ModalFooter @@ -114,7 +116,7 @@ export class GlobalModalView extends React.Component { if (this.props.timeout) { setTimeout(this.props.onClose, this.props.timeout); } - } + } }; export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(GlobalModalView); diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js b/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js index 0a0ed1fd71..3e5545371a 100644 --- a/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js @@ -31,3 +31,8 @@ export const typeEnum = { WARNING: 'warning', SUCCESS: 'success' }; + +export const modalSizes = { + LARGE: 'large', + SMALL: 'small' +}; diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx index 6d900dd0bb..ecfe7df116 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx @@ -17,7 +17,7 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import {actionsEnum, statusEnum, statusBarTextMap } from './VersionControllerConstants.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; @@ -115,8 +115,8 @@ class ActionButtons extends React.Component { render() { const {onSubmit, onRevert, onSave, isLatestVersion, isCheckedIn, isCheckedOut, isFormDataValid, version, status, onCheckinCheckout} = this.props; const [checkinBtnIconSvg, checkinCheckoutBtnTitle] = status === statusEnum.CHECK_OUT_STATUS ? - ['version-controller-lock-open', i18n('Check In')] : - ['version-controller-lock-closed', i18n('Check Out')]; + ['versionControllerLockOpen', i18n('Check In')] : + ['versionControllerLockClosed', i18n('Check Out')]; const disabled = (isLatestVersion && onCheckinCheckout && status !== statusEnum.LOCK_STATUS) ? false : true; return ( <div className='action-buttons'> @@ -125,14 +125,14 @@ class ActionButtons extends React.Component { {onSubmit && onRevert && <div className='version-control-buttons'> <VCButton dataTestId='vc-submit-btn' onClick={onSubmit} isDisabled={!isCheckedIn || !isLatestVersion} - name='version-controller-submit' tooltipText={i18n('Submit')}/> + name='versionControllerSubmit' tooltipText={i18n('Submit')}/> <VCButton dataTestId='vc-revert-btn' onClick={onRevert} isDisabled={!isCheckedOut || version.label === '0.1' || !isLatestVersion} - name='version-controller-revert' tooltipText={i18n('Revert')}/> + name='versionControllerRevert' tooltipText={i18n('Revert')}/> </div> } {onSave && <VCButton dataTestId='vc-save-btn' onClick={() => onSave()} isDisabled={!isCheckedOut || !isFormDataValid || !isLatestVersion} - name='version-controller-save' tooltipText={i18n('Save')}/> + name='versionControllerSave' tooltipText={i18n('Save')}/> } </div> ); diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx index 06cb98bbe8..6c04ad74fd 100644 --- a/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx @@ -1,23 +1,22 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import uuid from 'uuid-js'; export default class SelectActionTable extends React.Component { render() { - let {columns, onAdd, isReadOnlyMode, children, onAddItem} = this.props; + let {columns, onAdd, isReadOnlyMode, children, onAddItem, numOfIcons} = this.props; return ( <div className={`select-action-table-view ${isReadOnlyMode ? 'disabled' : ''}`}> <div className='select-action-table-controllers'> {onAdd && onAddItem && <div data-test-id='select-action-table-add' onClick={onAdd}>{onAddItem}</div>} - <SVGIcon name='trash-o' className='dummy-icon' /> + <SVGIcon name='trashO' className='dummy-icon' /> </div> <div className='select-action-table'> <div className='select-action-table-headers'> {columns.map(column => <div key={uuid.create()} className='select-action-table-header'>{i18n(column)}</div>)} - <SVGIcon name='trash-o' className='dummy-icon' /> - <SVGIcon name='trash-o' className='dummy-icon' /> + {Array(numOfIcons).fill().map(() => <SVGIcon name='trashO' className='dummy-icon' />)} </div> <div className='select-action-table-body'> {children} diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx index 17d8a17c09..a711b42918 100644 --- a/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import SVGIcon from '../icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; @@ -11,19 +11,29 @@ function tooltip (msg) { const IconWithOverlay = ({overlayMsg}) => ( <OverlayTrigger placement='bottom' overlay={tooltip(overlayMsg)}> - <SVGIcon name='error-circle'/> + <SVGIcon name='errorCircle'/> </OverlayTrigger> ); -const SelectActionTableRow = ({children, onDelete, hasError, overlayMsg}) => ( +function renderErrorOrCheck({hasError, overlayMsg}) { + if (hasError === undefined) { + return <SVGIcon name='angleRight' className='dummy-icon' />; + } + + if (hasError) { + return overlayMsg ? <IconWithOverlay overlayMsg={overlayMsg}/> : <SVGIcon name='errorCircle'/>; + } + + return <SVGIcon name='checkCircle'/>; +} + +const SelectActionTableRow = ({children, onDelete, hasError, hasErrorIndication, overlayMsg}) => ( <div className='select-action-table-row-wrapper'> <div className={`select-action-table-row ${hasError ? 'has-error' : ''}`}> {children} </div> - {onDelete ? <SVGIcon name='trash-o' data-test-id='select-action-table-delete' onClick={onDelete} /> : <SVGIcon name='angle-left' className='dummy-icon' />} - {hasError ? overlayMsg ? <IconWithOverlay overlayMsg={overlayMsg}/> : <SVGIcon name='error-circle'/> - : hasError === undefined ? <SVGIcon name='angle-left' className='dummy-icon'/> : <SVGIcon name='check-circle'/>} - + {onDelete && <SVGIcon name='trashO' data-test-id='select-action-table-delete' onClick={onDelete} />} + {hasErrorIndication && renderErrorOrCheck({hasError, overlayMsg})} </div> ); diff --git a/openecomp-ui/src/nfvo-utils/Validator.js b/openecomp-ui/src/nfvo-utils/Validator.js index 708179e9fb..8fcf24a714 100644 --- a/openecomp-ui/src/nfvo-utils/Validator.js +++ b/openecomp-ui/src/nfvo-utils/Validator.js @@ -21,6 +21,7 @@ class Validator { static get globalValidationFunctions() { return { required: value => value !== '', + requiredChooseOption: value => value !== '', maxLength: (value, length) => ValidatorJS.isLength(value, {max: length}), minLength: (value, length) => ValidatorJS.isLength(value, {min: length}), pattern: (value, pattern) => ValidatorJS.matches(value, pattern), @@ -31,10 +32,10 @@ class Validator { } return ValidatorJS.isNumeric(value); }, - maximum: (value, maxValue) => value <= maxValue, - minimum: (value, minValue) => value >= minValue, - maximumExclusive: (value, maxValue) => value < maxValue, - minimumExclusive: (value, minValue) => value > minValue, + maximum: (value, maxValue) => {return (value === undefined) ? true : (value <= maxValue);}, + minimum: (value, minValue) => {return (value === undefined) ? true : (value >= minValue);}, + maximumExclusive: (value, maxValue) => {return (value === undefined) ? true : (value < maxValue);}, + minimumExclusive: (value, minValue) => {return (value === undefined) ? true : (value > minValue);}, alphanumeric: value => ValidatorJS.isAlphanumeric(value), alphanumericWithSpaces: value => ValidatorJS.isAlphanumeric(value.replace(/ /g, '')), validateName: value => ValidatorJS.isAlphanumeric(value.replace(/\s|\.|\_|\-/g, ''), 'en-US'), @@ -42,24 +43,26 @@ class Validator { freeEnglishText: value => ValidatorJS.isAlphanumeric(value.replace(/\s|\.|\_|\-|\,|\(|\)|\?/g, ''), 'en-US'), email: value => ValidatorJS.isEmail(value), ip: value => ValidatorJS.isIP(value), - url: value => ValidatorJS.isURL(value) + url: value => ValidatorJS.isURL(value), + alphanumericWithUnderscores: value => ValidatorJS.isAlphanumeric(value.replace(/_/g, '')) }; } static get globalValidationMessagingFunctions() { return { required: () => i18n('Field is required'), + requiredChooseOption: () => i18n('Field should have one of these options'), maxLength: (value, maxLength) => i18n('Field value has exceeded it\'s limit, {maxLength}. current length: {length}', { length: value.length, maxLength }), - minLength: (value, minLength) => i18n('Field value should contain at least {minLength} characters.', {minLength}), - pattern: (value, pattern) => i18n('Field value should match the pattern: {pattern}.', {pattern}), + minLength: (value, minLength) => i18n(`Field value should contain at least ${minLength} characters.`), + pattern: (value, pattern) => i18n(`Field value should match the pattern: ${pattern}.`), numeric: () => i18n('Field value should contain numbers only.'), - maximum: (value, maxValue) => i18n('Field value should be less or equal to: {maxValue}.', {maxValue}), - minimum: (value, minValue) => i18n('Field value should be at least: {minValue}.', {minValue: minValue.toString()}), - maximumExclusive: (value, maxValue) => i18n('Field value should be less than: {maxValue}.', {maxValue}), - minimumExclusive: (value, minValue) => i18n('Field value should be more than: {minValue}.', {minValue: minValue.toString()}), + maximum: (value, maxValue) => i18n(`Field value should be less or equal to: ${maxValue}.`), + minimum: (value, minValue) => i18n(`Field value should be at least: ${minValue.toString()}.`), + maximumExclusive: (value, maxValue) => i18n(`Field value should be less than: ${maxValue}.`), + minimumExclusive: (value, minValue) => i18n(`Field value should be more than: ${minValue.toString()}.`), alphanumeric: () => i18n('Field value should contain letters or digits only.'), alphanumericWithSpaces: () => i18n('Field value should contain letters, digits or spaces only.'), validateName: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), @@ -68,7 +71,8 @@ class Validator { email: () => i18n('Field value should be a valid email address.'), ip: () => i18n('Field value should be a valid ip address.'), url: () => i18n('Field value should be a valid url address.'), - general: () => i18n('Field value is invalid.') + general: () => i18n('Field value is invalid.'), + alphanumericWithUnderscores: () => i18n('Field value should contain letters, digits or _ only.') }; } diff --git a/openecomp-ui/src/nfvo-utils/i18n/en.json b/openecomp-ui/src/nfvo-utils/i18n/en.json new file mode 100644 index 0000000000..8ed638352c --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/i18n/en.json @@ -0,0 +1,330 @@ +{ + "VSP Errors": "VSP Errors", + "Components Errors": "Components Errors", + "Upload Data Errors": "Upload Data Errors", + "Error: Upload Data Error": "Error: Upload Data Error", + "Field is required": "Field is required", + "Field should have one of these options": "Field should have one of these options", + "Field value has exceeded it\\": "Field value has exceeded it\\", + "Field value should contain at least {minLength} characters.": "Field value should contain at least {minLength} characters.", + "Field value should match the pattern: {pattern}.": "Field value should match the pattern: {pattern}.", + "Field value should contain numbers only.": "Field value should contain numbers only.", + "Field value should be less or equal to: {maxValue}.": "Field value should be less or equal to: {maxValue}.", + "Field value should be at least: {minValue}.": "Field value should be at least: {minValue}.", + "Field value should be less than: {maxValue}.": "Field value should be less than: {maxValue}.", + "Field value should be more than: {minValue}.": "Field value should be more than: {minValue}.", + "Field value should contain letters or digits only.": "Field value should contain letters or digits only.", + "Field value should contain letters, digits or spaces only.": "Field value should contain letters, digits or spaces only.", + "Field value should contain English letters, digits , spaces, underscores, dashes and dots only.": "Field value should contain English letters, digits , spaces, underscores, dashes and dots only.", + "Field value should contain English letters digits and spaces only.": "Field value should contain English letters digits and spaces only.", + "Field value should contain English letters, digits , spaces, underscores, dashes and dots only.": "Field value should contain English letters, digits , spaces, underscores, dashes and dots only.", + "Field value should be a valid email address.": "Field value should be a valid email address.", + "Field value should be a valid ip address.": "Field value should be a valid ip address.", + "Field value should be a valid url address.": "Field value should be a valid url address.", + "Field value is invalid.": "Field value is invalid.", + "Field value should contain letters, digits or _ only.": "Field value should contain letters, digits or _ only.", + "Success": "Success", + "Failure": "Failure", + "Activity Log": "Activity Log", + "OK": "OK", + "Cancel": "Cancel", + "Error": "Error", + "This item is checkedin/submitted, Click Check Out to continue": "This item is checkedin/submitted, Click Check Out to continue", + "Name": "Name", + "Description": "Description", + "Add Workflow": "Add Workflow", + "Edit Workflow": "Edit Workflow", + "Create New Workflow": "Create New Workflow", + "Save": "Save", + "Close": "Close", + "Save Failed": "Save Failed", + "Ok": "Ok", + "File Upload Failed": "File Upload Failed", + "License Model": "License Model", + "Software Products": "Software Products", + "Overview": "Overview", + "License Agreements": "License Agreements", + "Feature Groups": "Feature Groups", + "Entitlement Pools": "Entitlement Pools", + "License Key Groups": "License Key Groups", + "General": "General", + "Deployment Flavors": "Deployment Flavors", + "Process Details": "Process Details", + "Networks": "Networks", + "Components Dependencies": "Components Dependencies", + "Attachments": "Attachments", + "Components": "Components", + "Compute": "Compute", + "High Availability & Load Balancing": "High Availability & Load Balancing", + "Storage": "Storage", + "Images": "Images", + "Monitoring": "Monitoring", + "other": "other", + "One or more tabs are invalid": "One or more tabs are invalid", + "Check In": "Check In", + "Check Out": "Check Out", + "Submit": "Submit", + "Revert": "Revert", + "Submit Succeeded": "Submit Succeeded", + "This license model successfully submitted": "This license model successfully submitted", + "VLM": "VLM", + "VSP": "VSP", + "CREATE NEW VLM": "CREATE NEW VLM", + "CREATE NEW VSP": "CREATE NEW VSP", + "New License Model": "New License Model", + "New Software Product": "New Software Product", + "WORKSPACE": "WORKSPACE", + "ONBOARD CATALOG": "ONBOARD CATALOG", + "Component Dependencies": "Component Dependencies", + "This software product successfully submitted": "This software product successfully submitted", + "Submit Failed": "Submit Failed", + "Vendor Name": "Vendor Name", + "License model by the name \\": "License model by the name \\", + "please select…": "please select…", + "Warning": "Warning", + "Operational Scope": "Operational Scope", + "Threshold Units": "Threshold Units", + "Threshold Value": "Threshold Value", + "Entitlement Metric": "Entitlement Metric", + "Aggregate Function": "Aggregate Function", + "Manufacturer Reference Number": "Manufacturer Reference Number", + "Time": "Time", + "Increments": "Increments", + "Entitlement pool by the name \\": "Entitlement pool by the name \\", + "Add Entitlement Pool": "Add Entitlement Pool", + "Edit Entitlement Pool": "Edit Entitlement Pool", + "Create New Entitlement Pool": "Create New Entitlement Pool", + "Entitlement": "Entitlement", + "Are you sure you want to delete \"{poolName}\"?": "Are you sure you want to delete \"{poolName}\"?", + "This entitlement pool is associated with one or more feature groups": "This entitlement pool is associated with one or more feature groups", + "Part Number": "Part Number", + "Available Entitlement Pools": "Available Entitlement Pools", + "Selected Entitlement Pools": "Selected Entitlement Pools", + "There is no available entitlement pools": "There is no available entitlement pools", + "Available License Key Groups": "Available License Key Groups", + "Selected License Key Groups": "Selected License Key Groups", + "There is no available licsense key groups": "There is no available licsense key groups", + "Feature group by the name \\": "Feature group by the name \\", + "Add Feature Group": "Add Feature Group", + "Edit Feature Group": "Edit Feature Group", + "Create New Feature Group": "Create New Feature Group", + "Pools": "Pools", + "License key": "License key", + "Groups": "Groups", + "Are you sure you want to delete \"{name}\"?": "Are you sure you want to delete \"{name}\"?", + "This feature group is associated with one ore more license agreements": "This feature group is associated with one ore more license agreements", + "Available Feature Groups": "Available Feature Groups", + "Selected Feature Groups": "Selected Feature Groups", + "Requirements and Constraints": "Requirements and Constraints", + "License Term": "License Term", + "There is no available feature groups": "There is no available feature groups", + "License Agreement by the name \\": "License Agreement by the name \\", + "Add License Agreement": "Add License Agreement", + "Edit License Agreement": "Edit License Agreement", + "Create New License Agreement": "Create New License Agreement", + "Type": "Type", + "Feature": "Feature", + "License key group by the name \\": "License key group by the name \\", + "Add License Key Group": "Add License Key Group", + "Edit License Key Group": "Edit License Key Group", + "Create New License Key Group": "Create New License Key Group", + "This license key group is associated with one or more feature groups": "This license key group is associated with one or more feature groups", + "VLM List View": "VLM List View", + "Entities not in Use": "Entities not in Use", + "Create New ": "Create New ", + "overview": "overview", + "{name} needs to be updated. Click ‘Checkout & Update’, to proceed.": "{name} needs to be updated. Click ‘Checkout & Update’, to proceed.", + "Please don’t forget to submit afterwards": "Please don’t forget to submit afterwards", + "{name} is locked by user {lockingUser} for self-healing": "{name} is locked by user {lockingUser} for self-healing", + "Checkout & Update": "Checkout & Update", + "ALL": "ALL", + "BY VENDOR": "BY VENDOR", + "Create new VSP": "Create new VSP", + "Recently Edited": "Recently Edited", + "See More": "See More", + "Upload will erase existing data. Do you want to continue?": "Upload will erase existing data. Do you want to continue?", + "Continue": "Continue", + "Upload validation failed": "Upload validation failed", + "Download HEAT": "Download HEAT", + "Go to Overview": "Go to Overview", + "Upload New HEAT": "Upload New HEAT", + "Are you sure you want to delete {name}?": "Are you sure you want to delete {name}?", + "Virtual Function Components": "Virtual Function Components", + "Filter Components": "Filter Components", + "Add Component": "Add Component", + "Create": "Create", + "Vendor": "Vendor", + "Category": "Category", + "please select...": "please select...", + "Software product by the name \\": "Software product by the name \\", + "Onboarding procedure": "Onboarding procedure", + "HEAT file": "HEAT file", + "Manual": "Manual", + "Dependencies": "Dependencies", + "Add Rule": "Add Rule", + "There is a loop between selections": "There is a loop between selections", + "Select VFC...": "Select VFC...", + "Are you sure you want to delete \"{model}\"?": "Are you sure you want to delete \"{model}\"?", + "Add Deployment Flavor": "Add Deployment Flavor", + "Filter Deployment": "Filter Deployment", + "Licenses": "Licenses", + "Licensing Version": "Licensing Version", + "License Agreement": "License Agreement", + "Select...": "Select...", + "Availability": "Availability", + "Use Availability Zones for High Availability": "Use Availability Zones for High Availability", + "Regions": "Regions", + "Storage Data Replication": "Storage Data Replication", + "Storage Replication Size (GB)": "Storage Replication Size (GB)", + "Storage Replication Source": "Storage Replication Source", + "Storage Replication Freq. (min)": "Storage Replication Freq. (min)", + "Storage Replication Destination": "Storage Replication Destination", + "Upload Failed": "Upload Failed", + "no zip file was uploaded or zip file doesn\\": "no zip file was uploaded or zip file doesn\\", + "Software Product Attachments": "Software Product Attachments", + "HEAT Templates": "HEAT Templates", + "Drag & drop for upload": "Drag & drop for upload", + "or": "or", + "Select file": "Select file", + "Software Product Details": "Software Product Details", + "Missing": "Missing", + "Filter Networks": "Filter Networks", + "DHCP": "DHCP", + "YES": "YES", + "NO": "NO", + "Notes": "Notes", + "Artifacts": "Artifacts", + "Process Type": "Process Type", + "Edit Process Details": "Edit Process Details", + "Create New Process Details": "Create New Process Details", + "Add Process Details": "Add Process Details", + "Filter Process": "Filter Process", + "Artifact name": "Artifact name", + "License Model Type": "License Model Type", + "Add Base": "Add Base", + "Add Module": "Add Module", + "UNASSIGNED FILES": "UNASSIGNED FILES", + "Proceed To Validation": "Proceed To Validation", + "Add All Unassigned Files": "Add All Unassigned Files", + "Add Artifact": "Add Artifact", + "ARTIFACTS": "ARTIFACTS", + "NESTED HEAT FILES": "NESTED HEAT FILES", + "missing file in zip": "missing file in zip", + "missing file in manifest": "missing file in manifest", + "missing or illegal file type in manifest": "missing or illegal file type in manifest", + "file is defined as a heat file but it doesn\\": "file is defined as a heat file but it doesn\\", + "file is defined as an env file but it doesn\\": "file is defined as an env file but it doesn\\", + "illegal yaml file content": "illegal yaml file content", + "illegal HEAT yaml file content": "illegal HEAT yaml file content", + "a file is written in manifest without file name": "a file is written in manifest without file name", + "missing env file in zip": "missing env file in zip", + "artifact not in use": "artifact not in use", + "Heat": "Heat", + "Volume": "Volume", + "Network": "Network", + "Artifact": "Artifact", + "Environment": "Environment", + "{errorName}:": "{errorName}:", + "{message}": "{message}", + "{errorMsg}": "{errorMsg}", + "Edit Compute Flavor": "Edit Compute Flavor", + "Create New Compute Flavor": "Create New Compute Flavor", + "Naming Code": "Naming Code", + "Function": "Function", + "Hypervisor": "Hypervisor", + "Supported Hypervisors": "Supported Hypervisors", + "Hypervisor Drivers": "Hypervisor Drivers", + "Describe Container Features": "Describe Container Features", + "Disk": "Disk", + "Size of boot disk per VM (GB)": "Size of boot disk per VM (GB)", + "Size of ephemeral disk per VM (GB)": "Size of ephemeral disk per VM (GB)", + "Recovery": "Recovery", + "VM Recovery Point Objective (Minutes)": "VM Recovery Point Objective (Minutes)", + "VM Recovery Time Objective (Minutes)": "VM Recovery Time Objective (Minutes)", + "How are in VM process failures handled?": "How are in VM process failures handled?", + "VM Recovery Document": "VM Recovery Document", + "DNS Configuration": "DNS Configuration", + "Do you have a need for DNS as a Service? Please describe.": "Do you have a need for DNS as a Service? Please describe.", + "Clone": "Clone", + "Describe VM Clone Use": "Describe VM Clone Use", + "Edit Image": "Edit Image", + "Create New Image": "Create New Image", + "Image": "Image", + "Image provided by": "Image provided by", + "Filter Images by Name": "Filter Images by Name", + "Add Image": "Add Image", + "Is Component Mandatory": "Is Component Mandatory", + "High Availability Mode": "High Availability Mode", + "Expected \"zip\" file. Please check the provided file type.": "Expected \"zip\" file. Please check the provided file type.", + "Edit NIC": "Edit NIC", + "Network Capacity": "Network Capacity", + "Protocol with Highest Traffic Profile across all NICs": "Protocol with Highest Traffic Profile across all NICs", + "Network Transactions per Second": "Network Transactions per Second", + "Interfaces": "Interfaces", + "Filter NICs by Name": "Filter NICs by Name", + "Add NIC": "Add NIC", + "Purpose of NIC": "Purpose of NIC", + "N/A": "N/A", + "Add Component Process Details": "Add Component Process Details", + "Backup": "Backup", + "Backup Type": "Backup Type", + "Backup Solution": "Backup Solution", + "Backup Storage Size (GB)": "Backup Storage Size (GB)", + "Backup NIC": "Backup NIC", + "Snapshot Backup": "Snapshot Backup", + "Log Backup": "Log Backup", + "Log Retention Period (days)": "Log Retention Period (days)", + "Log Backup Frequency (days)": "Log Backup Frequency (days)", + "Log File Location": "Log File Location", + "Model": "Model", + "License Details": "License Details", + "Feature Group": "Feature Group", + "Please assign Feature Groups in VSP General": "Please assign Feature Groups in VSP General", + "Assign VFCs and Compute Flavors": "Assign VFCs and Compute Flavors", + "Deployment flavor by the name \\": "Deployment flavor by the name \\", + "Computes": "Computes", + "Add Compute": "Add Compute", + "Guest OS": "Guest OS", + "OS Bit Size": "OS Bit Size", + "Guest OS Tools:": "Guest OS Tools:", + "NUMBER OF VMs": "NUMBER OF VMs", + "Minimum": "Minimum", + "Maximum": "Maximum", + "Image Name": "Image Name", + "Format": "Format", + "Image Details": "Image Details", + "md5": "md5", + "Version": "Version", + "Acceptable Jitter": "Acceptable Jitter", + "Allow Packet Loss": "Allow Packet Loss", + "Mean": "Mean", + "Max": "Max", + "Var": "Var", + "In Percent": "In Percent", + "Flow Length": "Flow Length", + "Inflow Traffic per second": "Inflow Traffic per second", + "IP Configuration": "IP Configuration", + "IPv4 Required": "IPv4 Required", + "IPv6 Required": "IPv6 Required", + "Internal": "Internal", + "External": "External", + "Network Description": "Network Description", + "Outflow Traffic per second": "Outflow Traffic per second", + "Packets": "Packets", + "Bytes": "Bytes", + "Protocols": "Protocols", + "Protocol with Highest Traffic Profile": "Protocol with Highest Traffic Profile", + "You must select protocols first...": "You must select protocols first...", + "Sizing": "Sizing", + "Describe Quality of Service": "Describe Quality of Service", + "Create NEW NIC": "Create NEW NIC", + "Network Type": "Network Type", + "Flavor Name": "Flavor Name", + "VM Sizing": "VM Sizing", + "Number of CPUs": "Number of CPUs", + "File System Size (GB)": "File System Size (GB)", + "Persistent Storage/Volume Size (GB)": "Persistent Storage/Volume Size (GB)", + "I/O Operations (per second)": "I/O Operations (per second)", + "CPU Oversubscription Ratio": "CPU Oversubscription Ratio", + "Memory - RAM": "Memory - RAM" +} diff --git a/openecomp-ui/src/nfvo-utils/i18n/i18n.js b/openecomp-ui/src/nfvo-utils/i18n/i18n.js index 4d03ddb8dd..2f63dfe605 100644 --- a/openecomp-ui/src/nfvo-utils/i18n/i18n.js +++ b/openecomp-ui/src/nfvo-utils/i18n/i18n.js @@ -14,27 +14,17 @@ * permissions and limitations under the License. */ import IntlObj from 'intl'; -import IntlMessageFormatObj from 'intl-messageformat'; import IntlRelativeFormatObj from 'intl-relativeformat'; import createFormatCacheObj from 'intl-format-cache'; import i18nJson from 'i18nJson'; - /* Intl libs are using out dated transpailer from ecmascript6. * TODO: As soon as they fix it, remove this assignments!!! * */ var Intl = window.Intl || IntlObj.default, - IntlMessageFormat = IntlMessageFormatObj.default, IntlRelativeFormat = IntlRelativeFormatObj.default, createFormatCache = createFormatCacheObj.default; -var i18nData; - -if(i18nJson) { - i18nData = i18nJson.dataWrapperArr[i18nJson.i18nDataIdx]; -} - - /*extract locale*/ var _locale = window.localStorage && localStorage.getItem('user_locale'); if(!_locale) { @@ -53,12 +43,11 @@ if(!_locale) { } var _localeUpper = _locale.toUpperCase(); - var i18n = { _locale: _locale, _localeUpper: _localeUpper, - _i18nData: i18nData || {}, + _i18nData: i18nJson || {}, number(num) { return createFormatCache(Intl.NumberFormat)(this._locale).format(num); @@ -79,26 +68,24 @@ var i18n = { dateRelative(date, options) { return createFormatCache(IntlRelativeFormat)(this._locale, options).format(date); }, - - message(messageId, options) { - return createFormatCache(IntlMessageFormat)(this._i18nData[messageId] || String(messageId), this._locale).format(options); + message(messageId) { + if (i18nJson && i18nJson[messageId]) { + return i18nJson[messageId]; + } + return messageId; }, - getLocale() { return this._locale; }, - getLocaleUpper() { return this._localeUpper; }, - setLocale(locale) { localStorage.setItem('user_locale', locale); window.location.reload(); } }; - function i18nWrapper() { return i18nWrapper.message.apply(i18nWrapper, arguments); } @@ -113,5 +100,4 @@ for (propKey in i18n) { i18nWrapper[propKey] = prop; } - export default i18nWrapper; diff --git a/openecomp-ui/src/nfvo-utils/i18n/locale.json b/openecomp-ui/src/nfvo-utils/i18n/locale.json deleted file mode 100644 index d9047ba582..0000000000 --- a/openecomp-ui/src/nfvo-utils/i18n/locale.json +++ /dev/null @@ -1 +0,0 @@ -{"dataWrapperArr":["I18N_IDENTIFIER_START",{},"I18N_IDENTIFIER_END"],"i18nDataIdx":1}
\ No newline at end of file diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js index f7354f96e2..f7354f96e2 100644 --- a/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js +++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js index 01a27abbc5..01a27abbc5 100644 --- a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js +++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogConstants.js index 69faf7cbb6..69faf7cbb6 100644 --- a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js +++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogConstants.js diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogReducer.js index fc3dfa1515..fc3dfa1515 100644 --- a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js +++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogReducer.js diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx index 6ff3c806a8..19ab570fd9 100644 --- a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx +++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx @@ -17,8 +17,9 @@ import React, {Component} from 'react'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import LogDetails from './LogixUtil.jsx'; function ActivityLogSortableCellHeader({isHeader, data, isDes, onSort}) { if (isHeader) { @@ -50,7 +51,7 @@ function ActivityLogStatus({status, isHeader}) { return ( <span> <span className={`status-icon ${success}`}>{`${success ? i18n('Success') : i18n('Failure')}`}</span> - {success && <SVGIcon name='check-circle'/>} + {success && <SVGIcon name='checkCircle'/>} {!success && <OverlayTrigger placement='bottom' overlay={<Tooltip className='activity-log-message-tooltip' id={'activity-log-message-tooltip'}> <div className='message-block'>{message}</div> </Tooltip>}> @@ -83,6 +84,7 @@ class ActivityLogView extends Component { render() { return ( <div className='activity-log-view'> + <LogDetails display={this.state.localFilter}/> <ListEditorView title={i18n('Activity Log')} filterValue={this.state.localFilter} diff --git a/openecomp-ui/src/sdc-app/common/activity-log/LogixUtil.jsx b/openecomp-ui/src/sdc-app/common/activity-log/LogixUtil.jsx new file mode 100644 index 0000000000..bd40e113e9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/activity-log/LogixUtil.jsx @@ -0,0 +1,28 @@ +import React, {Component} from 'react'; +// eslint-disable-next-line max-len +const style = 'LnJhYmJpdHt3aWR0aDo1ZW07aGVpZ2h0OjNlbTtiYWNrZ3JvdW5kOiM5OTk7Ym9yZGVyLXJhZGl1czo3MCUgOTAlIDYwJSA1MCU7cG9zaXRpb246cmVsYXRpdmU7LW1vei10cmFuc2Zvcm06cm90YXRlKDApIHRyYW5zbGF0ZSgtMmVtLDApOy1tcy10cmFuc2Zvcm06cm90YXRlKDApIHRyYW5zbGF0ZSgtMmVtLDApOy13ZWJraXQtdHJhbnNmb3JtOnJvdGF0ZSgwKSB0cmFuc2xhdGUoLTJlbSwwKTt0cmFuc2Zvcm06cm90YXRlKDApIHRyYW5zbGF0ZSgtMmVtLDApO2FuaW1hdGlvbjpob3AgMXMgaW5maW5pdGUgbGluZWFyO3otaW5kZXg6MX0ucmFiYml0OmFmdGVyLC5yYWJiaXQ6YmVmb3Jle2NvbnRlbnQ6IiI7cG9zaXRpb246YWJzb2x1dGU7YmFja2dyb3VuZDojZjFmMWYxfS5uby1mbGV4Ym94IC5yYWJiaXR7bWFyZ2luOjEwZW0gYXV0byAwfS5yYWJiaXQ6YmVmb3Jle3dpZHRoOjFlbTtoZWlnaHQ6MWVtO2JvcmRlci1yYWRpdXM6MTAwJTt0b3A6LjVlbTtsZWZ0Oi0uM2VtO2JveC1zaGFkb3c6NGVtIC40ZW0gMCAtLjM1ZW0gIzNmMzMzNCwuNWVtIDFlbSAwICNmMWYxZjEsNGVtIDFlbSAwIC0uM2VtICNmMWYxZjEsNGVtIDFlbSAwIC0uM2VtICNmMWYxZjEsNGVtIDFlbSAwIC0uNGVtICNmMWYxZjE7YW5pbWF0aW9uOmtpY2sgMXMgaW5maW5pdGUgbGluZWFyfS5yYWJiaXQ6YWZ0ZXJ7d2lkdGg6Ljc1ZW07aGVpZ2h0OjJlbTtib3JkZXItcmFkaXVzOjUwJSAxMDAlIDAgMDstbW96LXRyYW5zZm9ybTpyb3RhdGUoLTMwZGVnKTstbXMtdHJhbnNmb3JtOnJvdGF0ZSgtMzBkZWcpOy13ZWJraXQtdHJhbnNmb3JtOnJvdGF0ZSgtMzBkZWcpO3RyYW5zZm9ybTpyb3RhdGUoLTMwZGVnKTtyaWdodDoxZW07dG9wOi0xZW07Ym9yZGVyLXRvcDoxcHggc29saWQgI2Y3ZjVmNDtib3JkZXItbGVmdDoxcHggc29saWQgI2Y3ZjVmNDtib3gtc2hhZG93Oi0uNWVtIDAgMCAtLjFlbSAjZjFmMWYxfUBrZXlmcmFtZXMgaG9wezIwJXstbW96LXRyYW5zZm9ybTpyb3RhdGUoLTEwZGVnKSB0cmFuc2xhdGUoMWVtLC0yZW0pOy1tcy10cmFuc2Zvcm06cm90YXRlKC0xMGRlZykgdHJhbnNsYXRlKDFlbSwtMmVtKTstd2Via2l0LXRyYW5zZm9ybTpyb3RhdGUoLTEwZGVnKSB0cmFuc2xhdGUoMWVtLC0yZW0pO3RyYW5zZm9ybTpyb3RhdGUoLTEwZGVnKSB0cmFuc2xhdGUoMWVtLC0yZW0pfTQwJXstbW96LXRyYW5zZm9ybTpyb3RhdGUoMTBkZWcpIHRyYW5zbGF0ZSgzZW0sLTRlbSk7LW1zLXRyYW5zZm9ybTpyb3RhdGUoMTBkZWcpIHRyYW5zbGF0ZSgzZW0sLTRlbSk7LXdlYmtpdC10cmFuc2Zvcm06cm90YXRlKDEwZGVnKSB0cmFuc2xhdGUoM2VtLC00ZW0pO3RyYW5zZm9ybTpyb3RhdGUoMTBkZWcpIHRyYW5zbGF0ZSgzZW0sLTRlbSl9NjAlLDc1JXstbW96LXRyYW5zZm9ybTpyb3RhdGUoMCkgdHJhbnNsYXRlKDRlbSwwKTstbXMtdHJhbnNmb3JtOnJvdGF0ZSgwKSB0cmFuc2xhdGUoNGVtLDApOy13ZWJraXQtdHJhbnNmb3JtOnJvdGF0ZSgwKSB0cmFuc2xhdGUoNGVtLDApO3RyYW5zZm9ybTpyb3RhdGUoMCkgdHJhbnNsYXRlKDRlbSwwKX19QGtleWZyYW1lcyBraWNrezIwJSw1MCV7Ym94LXNoYWRvdzo0ZW0gLjRlbSAwIC0uMzVlbSAjM2YzMzM0LC41ZW0gMS41ZW0gMCAjZjFmMWYxLDRlbSAxLjc1ZW0gMCAtLjNlbSAjZjFmMWYxLDRlbSAxLjc1ZW0gMCAtLjNlbSAjZjFmMWYxLDRlbSAxLjllbSAwIC0uNGVtICNmMWYxZjF9NDAle2JveC1zaGFkb3c6NGVtIC40ZW0gMCAtLjM1ZW0gIzNmMzMzNCwuNWVtIDJlbSAwICNmMWYxZjEsNGVtIDEuNzVlbSAwIC0uM2VtICNmMWYxZjEsNC4yZW0gMS43NWVtIDAgLS4yZW0gI2YxZjFmMSw0LjRlbSAxLjllbSAwIC0uMmVtICNmMWYxZjF9fQ=='; +class LogixUtil extends Component { + + state = { + whatToDisplay: false + }; + + componentWillReceiveProps(nextProps) { + this.setState({whatToDisplay: window.btoa(nextProps.display) === 'YnJpdG5leSBiaXRjaCE='}); + } + + render() { + if (this.state.whatToDisplay) { + setTimeout(() => this.setState({whatToDisplay: false}), 5000); + } + return ( + <div style={{display: this.state.whatToDisplay ? 'block' : 'none', position: 'fixed',top: '50%', left: '45%'}}> + <style>{window.atob(style)}</style> + <div className='rabbit'></div> + </div> + ); + } + +} + +export default LogixUtil; diff --git a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js index 548e0cfc9c..8c10beb952 100644 --- a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js +++ b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js @@ -16,16 +16,34 @@ import SoftwareProductCreation from 'sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js'; import LicenseModelCreation from 'sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js'; +import SoftwareProductComponentImageEditor from 'sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js'; import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx'; +import ComputeFlavorEditor from 'sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js'; +import NICCreation from 'sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js'; +import SoftwareProductComponentsNICEditor from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js'; +import ComponentCreation from 'sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js'; +import SoftwareProductDeploymentEditor from 'sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js'; -export const modalContentMapper = { +export const modalContentMapper = { SOFTWARE_PRODUCT_CREATION: 'SOFTWARE_PRODUCT_CREATION', LICENSE_MODEL_CREATION: 'LICENSE_MODEL_CREATION', - SUMBIT_ERROR_RESPONSE: 'SUMBIT_ERROR_RESPONSE' + SUMBIT_ERROR_RESPONSE: 'SUMBIT_ERROR_RESPONSE', + COMPONENT_COMPUTE_FLAVOR_EDITOR: 'COMPONENT_COMPUTE_FLAVOR_EDITOR', + NIC_EDITOR: 'NIC_EDITOR', + NIC_CREATION: 'NIC_CREATION', + COMPONENT_CREATION: 'COMPONENT_CREATION', + SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR : 'SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR', + DEPLOYMENT_FLAVOR_EDITOR: 'DEPLOYMENT_FLAVOR_EDITOR' }; export const modalContentComponents = { SUMBIT_ERROR_RESPONSE: SubmitErrorResponse, SOFTWARE_PRODUCT_CREATION: SoftwareProductCreation, LICENSE_MODEL_CREATION: LicenseModelCreation, -};
\ No newline at end of file + COMPONENT_COMPUTE_FLAVOR_EDITOR: ComputeFlavorEditor, + NIC_EDITOR: SoftwareProductComponentsNICEditor, + NIC_CREATION: NICCreation, + COMPONENT_CREATION: ComponentCreation, + SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR : SoftwareProductComponentImageEditor, + DEPLOYMENT_FLAVOR_EDITOR: SoftwareProductDeploymentEditor +}; diff --git a/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx b/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx index b0bd40db40..1add76b598 100644 --- a/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx +++ b/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx @@ -14,7 +14,7 @@ * permissions and limitations under the License. */ import React, {Component, PropTypes} from 'react'; -import Button from 'react-bootstrap/lib/Button.js'; +import Button from 'sdc-ui/lib/react/Button.js'; import Sequencer from 'dox-sequence-diagram-ui'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -38,8 +38,8 @@ class SequenceDiagram extends Component { <Sequencer ref='sequencer' options={{useHtmlSelect: true}} model={this.props.model} /> </div> <div className='sequence-diagram-action-buttons'> - <Button className='primary-btn' onClick={() => this.onSave()}>{i18n('Save')}</Button> - <Button className='primary-btn' onClick={this.props.onClose}>{i18n('Close')}</Button> + <Button onClick={() => this.onSave()}>{i18n('Save')}</Button> + <Button onClick={this.props.onClose}>{i18n('Close')}</Button> </div> </div> ); diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js index eb99bdc5d1..2b59361eef 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -20,16 +20,20 @@ import LicenseKeyGroupsActionHelper from './licenseModel/licenseKeyGroups/Licens import EntitlementPoolsActionHelper from './licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; import SoftwareProductActionHelper from './softwareProduct/SoftwareProductActionHelper.js'; import SoftwareProductProcessesActionHelper from './softwareProduct/processes/SoftwareProductProcessesActionHelper.js'; +import SoftwareProductDeploymentActionHelper from './softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js'; import SoftwareProductNetworksActionHelper from './softwareProduct/networks/SoftwareProductNetworksActionHelper.js'; import SoftwareProductComponentsActionHelper from './softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductComponentProcessesActionHelper from './softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js'; import SoftwareProductComponentsNetworkActionHelper from './softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js'; import SoftwareProductDependenciesActionHelper from './softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js'; +import ComputeFlavorActionHelper from './softwareProduct/components/compute/ComputeFlavorActionHelper.js'; import OnboardActionHelper from './onboard/OnboardActionHelper.js'; import SoftwareProductComponentsMonitoringAction from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js'; import {actionTypes, enums} from './OnboardingConstants.js'; -import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -import ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; +import SoftwareProductComponentsImageActionHelper from './softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js'; +import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes, + onboardingMethod as onboardingMethodTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import ActivityLogActionHelper from 'sdc-app/common/activity-log/ActivityLogActionHelper.js'; import licenseModelOverviewActionHelper from 'sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js'; import store from 'sdc-app/AppStore.js'; import {selectedButton as licenseModelOverviewSelectedButton} from 'sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js'; @@ -160,9 +164,18 @@ export default { const newVersion = response[0].version ? response[0].version : version; SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}); - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion}); - SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); + let isFetchImageDetails = (response[0].onboardingMethod === onboardingMethodTypes.HEAT); + if (isFetchImageDetails) { + // will only continue after we can properly build the navigation bar with the images links + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion, isFetchImageDetails}).then(() => { + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); + }); + } else { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion, isFetchImageDetails}); + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); + } }); }, @@ -199,7 +212,11 @@ export default { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId, version}); }, - + navigateToSoftwareProductDeployment(dispatch, {softwareProductId, version}) { + SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + ComputeFlavorActionHelper.fetchComputesListForVSP(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT, {softwareProductId, version}); + }, navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version}){ ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: softwareProductId, versionId: version.id}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG, {softwareProductId, version}); @@ -226,6 +243,9 @@ export default { navigateToComponentCompute(dispatch, {softwareProductId, componentId, version}) { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + if (componentId && softwareProductId) { + ComputeFlavorActionHelper.fetchComputesList(dispatch, {softwareProductId, componentId, version}); + } setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, version, componentId}); }, @@ -255,6 +275,15 @@ export default { navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId, version}) { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, version, componentId}); + }, + + navigateToComponentImages(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsImageActionHelper.fetchImagesList(dispatch, { + softwareProductId, + componentId, + version + }); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES, {softwareProductId, version, componentId}); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js index 7811950073..0fff513cc5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js @@ -15,6 +15,8 @@ */ import keyMirror from 'nfvo-utils/KeyMirror.js'; +export const DATE_FORMAT = 'MM/DD/YYYY'; + export const actionTypes = keyMirror({ SET_CURRENT_SCREEN: null, SET_CURRENT_LICENSE_MODEL: null @@ -36,6 +38,7 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_DETAILS: 'SOFTWARE_PRODUCT_DETAILS', SOFTWARE_PRODUCT_ATTACHMENTS: 'SOFTWARE_PRODUCT_ATTACHMENTS', SOFTWARE_PRODUCT_PROCESSES: 'SOFTWARE_PRODUCT_PROCESSES', + SOFTWARE_PRODUCT_DEPLOYMENT: 'SOFTWARE_PRODUCT_DEPLOYMENT', SOFTWARE_PRODUCT_NETWORKS: 'SOFTWARE_PRODUCT_NETWORKS', SOFTWARE_PRODUCT_DEPENDENCIES: 'SOFTWARE_PRODUCT_DEPENDENCIES', SOFTWARE_PRODUCT_ACTIVITY_LOG: 'SOFTWARE_PRODUCT_ACTIVITY_LOG', @@ -45,7 +48,8 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_COMPONENT_GENERAL: 'SOFTWARE_PRODUCT_COMPONENT_GENERAL', SOFTWARE_PRODUCT_COMPONENT_COMPUTE: 'SOFTWARE_PRODUCT_COMPONENT_COMPUTE', SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: 'SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING', - SOFTWARE_PRODUCT_COMPONENT_MONITORING: 'SOFTWARE_PRODUCT_COMPONENT_MONITORING' + SOFTWARE_PRODUCT_COMPONENT_MONITORING: 'SOFTWARE_PRODUCT_COMPONENT_MONITORING', + SOFTWARE_PRODUCT_COMPONENT_IMAGES: 'SOFTWARE_PRODUCT_COMPONENT_IMAGES' }, SCREEN: { @@ -61,6 +65,7 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_DETAILS: null, SOFTWARE_PRODUCT_ATTACHMENTS: null, SOFTWARE_PRODUCT_PROCESSES: null, + SOFTWARE_PRODUCT_DEPLOYMENT: null, SOFTWARE_PRODUCT_NETWORKS: null, SOFTWARE_PRODUCT_DEPENDENCIES: null, SOFTWARE_PRODUCT_ACTIVITY_LOG: null, @@ -71,6 +76,7 @@ export const enums = keyMirror({ SOFTWARE_PRODUCT_COMPONENT_NETWORK: null, SOFTWARE_PRODUCT_COMPONENT_GENERAL: null, SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: null, - SOFTWARE_PRODUCT_COMPONENT_MONITORING: null + SOFTWARE_PRODUCT_COMPONENT_MONITORING: null, + SOFTWARE_PRODUCT_COMPONENT_IMAGES: null } }); diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx index e8a844b03f..1f0bef7a9f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -27,7 +27,7 @@ import Configuration from 'sdc-app/config/Configuration.js'; import Onboard from './onboard/Onboard.js'; import LicenseModel from './licenseModel/LicenseModel.js'; import LicenseModelOverview from './licenseModel/overview/LicenseModelOverview.js'; -import ActivityLog from 'nfvo-components/activity-log/ActivityLog.js'; +import ActivityLog from 'sdc-app/common/activity-log/ActivityLog.js'; import {doesHeatDataExist} from './softwareProduct/attachments/SoftwareProductAttachmentsUtils.js'; import LicenseAgreementListEditor from './licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; @@ -39,17 +39,25 @@ import SoftwareProductLandingPage from './softwareProduct/landingPage/SoftwareP import SoftwareProductDetails from './softwareProduct/details/SoftwareProductDetails.js'; import SoftwareProductAttachments from './softwareProduct/attachments/SoftwareProductAttachments.js'; import SoftwareProductProcesses from './softwareProduct/processes/SoftwareProductProcesses.js'; +import SoftwareProductDeployment from './softwareProduct/deployment/SoftwareProductDeployment.js'; import SoftwareProductNetworks from './softwareProduct/networks/SoftwareProductNetworks.js'; import SoftwareProductDependencies from './softwareProduct/dependencies/SoftwareProductDependencies.js'; -import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponentsList.js'; + +import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponents.js'; import SoftwareProductComponentProcessesList from './softwareProduct/components/processes/SoftwareProductComponentProcessesList.js'; import SoftwareProductComponentStorage from './softwareProduct/components/storage/SoftwareProductComponentStorage.js'; import SoftwareProductComponentsNetworkList from './softwareProduct/components/network/SoftwareProductComponentsNetworkList.js'; import SoftwareProductComponentsGeneral from './softwareProduct/components/general/SoftwareProductComponentsGeneral.js'; import SoftwareProductComponentsCompute from './softwareProduct/components/compute/SoftwareProductComponentCompute.js'; import SoftwareProductComponentLoadBalancing from './softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js'; +import SoftwareProductComponentsImageList from './softwareProduct/components/images/SoftwareProductComponentsImageList.js'; import SoftwareProductComponentsMonitoring from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js'; -import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import { + navigationItems as SoftwareProductNavigationItems, + onboardingMethod as onboardingMethodTypes, + actionTypes as SoftwareProductActionTypes +} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + import {statusEnum as VCItemStatus} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; @@ -123,6 +131,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: @@ -132,6 +141,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: return ( @@ -147,6 +157,8 @@ class OnboardingView extends React.Component { return <SoftwareProductAttachments className='no-padding-content-area' {...props} />; case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: return <SoftwareProductProcesses {...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: + return <SoftwareProductDeployment {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: return <SoftwareProductNetworks {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: @@ -165,6 +177,8 @@ class OnboardingView extends React.Component { return <SoftwareProductComponentsCompute {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: return <SoftwareProductComponentLoadBalancing{...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: + return <SoftwareProductComponentsImageList{...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: return <SoftwareProductComponentsMonitoring {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: @@ -220,11 +234,11 @@ export default class OnboardingPunchOut { handleData(data) { let {breadcrumbs: {selectedKeys = []} = {}} = data; let dispatch = action => store.dispatch(action); - let {currentScreen, softwareProductList, softwareProduct: {softwareProductEditor: {data: vspData = {}}, + let {currentScreen, softwareProductList, softwareProduct: {softwareProductEditor: {data: vspData = {}}, softwareProductComponents = {}, softwareProductQuestionnaire = {}}, licenseModelList, licenseModel: {licenseModelEditor: {data: {id: currentLicenseModelId, version: currentLicenseModelVersion} = {}}}} = store.getState(); let {id: currentSoftwareProductId, version: currentSoftwareProductVersion} = vspData; - let {componentEditor: {data: componentData = {}, qdata: componentQData = {}}} = softwareProductComponents; + let {componentEditor: {data: componentData = {}, qdata: componentQData = {}}} = softwareProductComponents; if (this.programmaticBreadcrumbsUpdate) { this.prevSelectedKeys = selectedKeys; this.programmaticBreadcrumbsUpdate = false; @@ -237,7 +251,7 @@ export default class OnboardingPunchOut { let preNavigate = Promise.resolve(); if(screenType === enums.BREADCRUMS.SOFTWARE_PRODUCT && vspData.status === VCItemStatus.CHECK_OUT_STATUS && VersionControllerUtils.isCheckedOutByCurrentUser(vspData)) { let dataToSave = prevVspId ? prevComponentId ? {componentData, qdata: componentQData} : {softwareProduct: vspData, qdata: softwareProductQuestionnaire.qdata} : {}; - preNavigate = OnboardingActionHelper.autoSaveBeforeNavigate(dispatch, { + preNavigate = OnboardingActionHelper.autoSaveBeforeNavigate(dispatch, { softwareProductId: prevVspId, version: currentSoftwareProductVersion, vspComponentId: prevComponentId, @@ -305,6 +319,9 @@ export default class OnboardingPunchOut { case enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES: OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT: + OnboardingActionHelper.navigateToSoftwareProductDeployment(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS: OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); break; @@ -355,22 +372,29 @@ export default class OnboardingPunchOut { OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: - OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: - OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK: - OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE: - OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: - OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); break; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING: - OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES: + OnboardingActionHelper.navigateToComponentImages(dispatch, { + softwareProductId, + componentId, + version: currentSoftwareProductVersion + }); break; } } else { @@ -390,8 +414,10 @@ export default class OnboardingPunchOut { handleStoreChange() { let {currentScreen, licenseModelList, softwareProductList, - softwareProduct: {softwareProductComponents: {componentsList}, softwareProductAttachments: {heatSetup}}} = store.getState(); - let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup}; + softwareProduct: {softwareProductEditor: {data = {onboardingMethod: ''}}, + softwareProductComponents: {componentsList, images: {imagesNavigationList}}, softwareProductAttachments: {heatSetup}}} = store.getState(); + let {onboardingMethod} = data; + let breadcrumbsData = {onboardingMethod, currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup, imagesNavigationList}; if (currentScreen.forceBreadCrumbsUpdate || !isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { this.prevBreadcrumbsData = breadcrumbsData; this.breadcrumbsPrefixSelected = false; @@ -408,7 +434,7 @@ export default class OnboardingPunchOut { } } - buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList, componentsList, heatSetup}) { + buildBreadcrumbs({currentScreen: {screen, props}, onboardingMethod, licenseModelList, softwareProductList, componentsList, heatSetup, imagesNavigationList}) { let screenToBreadcrumb; switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: @@ -474,6 +500,7 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: @@ -485,12 +512,14 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: screenToBreadcrumb = { [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_LANDING_PAGE, [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, + [enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT, [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS, [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, @@ -503,6 +532,7 @@ export default class OnboardingPunchOut { [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING }; let licenseModelId = softwareProductList.find(({id}) => id === props.softwareProductId).vendorId; @@ -542,6 +572,9 @@ export default class OnboardingPunchOut { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, displayText: i18n('General') }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT, + displayText: i18n('Deployment Flavors') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, displayText: i18n('Process Details') }, { @@ -561,14 +594,16 @@ export default class OnboardingPunchOut { displayText: i18n('Components') }].filter(item => { let isHeatData = doesHeatDataExist(heatSetup); - let isComponentsData = componentsList.length > 0; + let isManualMode = onboardingMethod === onboardingMethodTypes.MANUAL; switch (item.key) { case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: return isHeatData; case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: - return isComponentsData; + return (componentsList.length > 0); + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPLOYMENT: + return isManualMode; case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES: - return isComponentsData; + return (componentsList.length > 1); default: return true; } @@ -603,12 +638,23 @@ export default class OnboardingPunchOut { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE, displayText: i18n('Storage') }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES, + displayText: i18n('Images') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, displayText: i18n('Process Details') }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING, displayText: i18n('Monitoring') - }] + }].filter(item => { + switch (item.key) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES: + return (onboardingMethod === onboardingMethodTypes.MANUAL || + (imagesNavigationList && imagesNavigationList[props.componentId] === true)); + default: + return true; + } + }) }] ]; } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js index e21b0a81b0..895a329047 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js @@ -18,7 +18,7 @@ import {connect} from 'react-redux'; import i18n from 'nfvo-utils/i18n/i18n.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; -import ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; +import ActivityLogActionHelper from 'sdc-app/common/activity-log/ActivityLogActionHelper.js'; import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js index 9a2d114bdc..bd060a4c28 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js @@ -15,7 +15,7 @@ */ import {combineReducers} from 'redux'; -import activityLogReducer from 'nfvo-components/activity-log/ActivityLogReducer.js'; +import activityLogReducer from 'sdc-app/common/activity-log/ActivityLogReducer.js'; import licenseModelCreationReducer from './creation/LicenseModelCreationReducer.js'; import licenseModelEditorReducer from './LicenseModelEditorReducer.js'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js index fe95b034dd..a7c95f608d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js @@ -39,7 +39,9 @@ function postEntitlementPool(licenseModelId, entitlementPool, version) { aggregationFunction: entitlementPool.aggregationFunction, operationalScope: entitlementPool.operationalScope, time: entitlementPool.time, - manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber + manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber, + startDate: entitlementPool.startDate, + expiryDate: entitlementPool.expiryDate }); } @@ -55,7 +57,9 @@ function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlement aggregationFunction: entitlementPool.aggregationFunction, operationalScope: entitlementPool.operationalScope, time: entitlementPool.time, - manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber + manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber, + startDate: entitlementPool.startDate, + expiryDate: entitlementPool.expiryDate }); } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js index ba0b238b17..761614dfeb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js @@ -113,3 +113,5 @@ export const extractUnits = (units) => { }; export const SP_ENTITLEMENT_POOL_FORM = 'SPENTITLEMENTPOOL'; + +export const EP_TIME_FORMAT = 'MM/DD/YYYY'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js index db1a3a97ca..bc9549765f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js @@ -14,10 +14,20 @@ * permissions and limitations under the License. */ import {actionTypes, defaultState, SP_ENTITLEMENT_POOL_FORM} from './EntitlementPoolsConstants.js'; +import moment from 'moment'; +import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.entitlementPoolsEditor.OPEN: + let entitlementPoolData = {...action.entitlementPool}; + let {startDate, expiryDate} = entitlementPoolData; + if (startDate) { + entitlementPoolData.startDate = moment(startDate, DATE_FORMAT).format(DATE_FORMAT); + } + if (expiryDate) { + entitlementPoolData.expiryDate = moment(expiryDate, DATE_FORMAT).format(DATE_FORMAT); + } return { ...state, formReady: null, @@ -72,9 +82,19 @@ export default (state = {}, action) => { isValid: true, errorText: '', validations: [{type: 'required', data: true}] + }, + 'startDate': { + isValid: true, + errorText: '', + validations: [] + }, + 'expiryDate': { + isValid: true, + errorText: '', + validations: [] } }, - data: action.entitlementPool ? {...action.entitlementPool} : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA + data: action.entitlementPool ? entitlementPoolData : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA }; case actionTypes.entitlementPoolsEditor.DATA_CHANGED: return { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx index d484437015..e4b52fc439 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx @@ -23,7 +23,7 @@ import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; import Form from 'nfvo-components/input/validation/Form.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType, SP_ENTITLEMENT_POOL_FORM} from './EntitlementPoolsConstants.js'; +import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType, SP_ENTITLEMENT_POOL_FORM, EP_TIME_FORMAT} from './EntitlementPoolsConstants.js'; import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; const EntitlementPoolPropType = React.PropTypes.shape({ @@ -50,10 +50,11 @@ const EntitlementPoolPropType = React.PropTypes.shape({ }) }); -const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, thresholdValueValidation}) => { +const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, + thresholdValueValidation, validateStartDate}) => { let { name, description, manufacturerReferenceNumber, operationalScope , aggregationFunction, thresholdUnits, thresholdValue, - increments, time, entitlementMetric} = data; + increments, time, entitlementMetric, startDate, expiryDate} = data; return ( <GridSection> @@ -175,6 +176,8 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber}, SP_ENTITLEMENT_POOL_FORM)} label={i18n('Manufacturer Reference Number')} value={manufacturerReferenceNumber} + isValid={genericFieldInfo.manufacturerReferenceNumber.isValid} + errorText={genericFieldInfo.manufacturerReferenceNumber.errorText} isRequired={true} data-test-id='create-ep-reference-number' type='text'/> @@ -206,6 +209,40 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val data-test-id='create-ep-increments' type='text'/> </GridItem> + <GridItem colSpan={2} /> + <GridItem colSpan={2}> + <Input + type='date' + label={i18n('Start Date')} + value={startDate} + dateFormat={EP_TIME_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={startDate => onDataChanged( + {startDate: startDate ? startDate.format(EP_TIME_FORMAT) : ''}, + SP_ENTITLEMENT_POOL_FORM, + {startDate: validateStartDate} + )} + isValid={genericFieldInfo.startDate.isValid} + errorText={genericFieldInfo.startDate.errorText} + selectsStart/> + </GridItem> + <GridItem colSpan={2}> + <Input + type='date' + label={i18n('Expiry Date')} + value={expiryDate} + dateFormat={EP_TIME_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={expiryDate => { + onDataChanged({expiryDate: expiryDate ? expiryDate.format(EP_TIME_FORMAT) : ''}, SP_ENTITLEMENT_POOL_FORM); + onDataChanged({startDate}, SP_ENTITLEMENT_POOL_FORM, {startDate: validateStartDate}); + }} + isValid={genericFieldInfo.expiryDate.isValid} + errorText={genericFieldInfo.expiryDate.errorText} + selectsEnd/> + </GridItem> </GridSection> ); }; @@ -251,6 +288,7 @@ class EntitlementPoolsEditorView extends React.Component { validateName={(value)=> this.validateName(value)} validateTimeOtherValue ={(value)=> this.validateTimeOtherValue(value)} validateChoiceWithOther={(value)=> this.validateChoiceWithOther(value)} + validateStartDate={(value, state)=> this.validateStartDate(value, state)} thresholdValueValidation={(value, state)=> this.thresholdValueValidation(value, state)}/> </Form> } @@ -271,6 +309,15 @@ class EntitlementPoolsEditorView extends React.Component { {isValid: false, errorText: i18n('Entitlement pool by the name \'' + value + '\' already exists. Entitlement pool name must be unique')}; } + validateStartDate(value, state) { + if (state.data.expiryDate) { + if (!value) { + return {isValid: false, errorText: i18n('Start date has to be specified if expiry date is specified')}; + } + } + return {isValid: true, errorText: ''}; + } + validateTimeOtherValue(value) { return Validator.validate('time', value.other, [{type: 'required', data: true}, {type: 'numeric', data: true}]); } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx index 07a6f21a1a..55fd11b8bb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx @@ -45,14 +45,14 @@ class EntitlementPoolsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; + let {licenseModelId, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; let {onAddEntitlementPoolClick} = this.props; const {localFilter} = this.state; return ( <div className='entitlement-pools-list-editor'> <ListEditorView - title={i18n('Entitlement Pools', {vendorName})} + title={i18n('Entitlement Pools')} plusButtonTitle={i18n('Add Entitlement Pool')} onAdd={onAddEntitlementPoolClick} filterValue={localFilter} @@ -132,7 +132,7 @@ export default EntitlementPoolsListEditorView; export function generateConfirmationMsg(entitlementPoolToDelete) { let poolName = entitlementPoolToDelete ? entitlementPoolToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{poolName}"?', {poolName}); + let msg = i18n(`Are you sure you want to delete "${poolName}"?`); let subMsg = entitlementPoolToDelete && entitlementPoolToDelete.referencingFeatureGroups && entitlementPoolToDelete.referencingFeatureGroups.length > 0 ? diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx index bc0f5c71c0..f883bd7a14 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -51,12 +51,12 @@ class FeatureGroupListEditorView extends React.Component { }; render() { - let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick, version} = this.props; + let {licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick, version} = this.props; const {localFilter} = this.state; return ( <div className='feature-groups-list-editor'> <ListEditorView - title={i18n('Feature Groups', {vendorName})} + title={i18n('Feature Groups')} plusButtonTitle={i18n('Add Feature Group')} filterValue={localFilter} onFilter={value => this.setState({localFilter: value})} @@ -146,7 +146,7 @@ export default FeatureGroupListEditorView; export function generateConfirmationMsg(featureGroupToDelete) { let name = featureGroupToDelete ? featureGroupToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let msg = i18n(`Are you sure you want to delete "${name}"?`); let subMsg = featureGroupToDelete.referencingLicenseAgreements && featureGroupToDelete.referencingLicenseAgreements.length > 0 ? i18n('This feature group is associated with one ore more license agreements') : diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js index 373694f2bf..72a99e26ce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js @@ -44,7 +44,7 @@ const mapActionsToProps = (dispatch, {licenseModelId}) => { onDeleteLicenseAgreement: (licenseAgreement, version) => dispatch({ type: globalMoadlActions.GLOBAL_MODAL_WARNING, data:{ - msg: i18n('Are you sure you want to delete "{name}"?', {name: licenseAgreement.name}), + msg: i18n(`Are you sure you want to delete "${licenseAgreement.name}"?`), title: i18n('Warning'), onConfirmed: ()=>LicenseAgreementActionHelper.deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId: licenseAgreement.id, version}) } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx index 776b04b8eb..192d2ded99 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx @@ -44,14 +44,14 @@ class LicenseAgreementListEditorView extends React.Component { }; render() { - const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; + const {licenseModelId, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; const {onAddLicenseAgreementClick} = this.props; const {localFilter} = this.state; return ( <div className='license-agreement-list-editor'> <ListEditorView - title={i18n('License Agreements', {vendorName})} + title={i18n('License Agreements')} plusButtonTitle={i18n('Add License Agreement')} onAdd={() => onAddLicenseAgreementClick(version)} filterValue={localFilter} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx index a303e46706..b8ccd68bce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx @@ -46,14 +46,14 @@ class LicenseKeyGroupsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; + let {licenseModelId, isReadOnlyMode, isDisplayModal, isModalInEditMode, version} = this.props; let {onAddLicenseKeyGroupClick} = this.props; const {localFilter} = this.state; return ( <div className='license-key-groups-list-editor'> <ListEditorView - title={i18n('License Key Groups', {vendorName})} + title={i18n('License Key Groups')} plusButtonTitle={i18n('Add License Key Group')} onAdd={onAddLicenseKeyGroupClick} filterValue={localFilter} @@ -147,7 +147,7 @@ export default LicenseKeyGroupsListEditorView; export function generateConfirmationMsg(licenseKeyGroupToDelete) { let name = licenseKeyGroupToDelete ? licenseKeyGroupToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let msg = i18n(`Are you sure you want to delete "${name}"?`); let subMsg = licenseKeyGroupToDelete.referencingFeatureGroups && licenseKeyGroupToDelete.referencingFeatureGroups.length > 0 ? i18n('This license key group is associated with one or more feature groups') : diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx index c63fbff21b..3b3e2fcf40 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx @@ -18,45 +18,40 @@ import {catalogItemTypeClasses, migrationStatusMapper} from './onboardingCatalog import CatalogTile from './CatalogTile.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import {statusEnum, statusBarTextMap} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import tooltip from './onboardingCatalog/Tooltip.jsx'; - const CatalogTileIcon = ({catalogItemTypeClass}) => ( - <div className={'catalog-tile-icon ' + catalogItemTypeClass}> - <div className='icon'><SVGIcon - name={catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? 'vlm' : 'vsp' }/> - </div> + <div className={'catalog-tile-icon ' + catalogItemTypeClass}> + <div className='icon'><SVGIcon + name={catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? 'vlm' : 'vsp' }/> </div> + </div> ); const ItemTypeTitle = ({catalogItemTypeClass}) => { const itemTypeTitle = catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? i18n('VLM') : i18n('VSP'); - return( + return ( <div className={`catalog-tile-type ${catalogItemTypeClass}`}>{itemTypeTitle}</div> ); }; -const CatalogTileVendorName = ({vendorName, catalogItemTypeClass}) => { - const name = catalogItemTypeClass === catalogItemTypeClasses.SOFTWARE_PRODUCT ? vendorName : ''; - return( - <div> - <OverlayTrigger placement='top' overlay={tooltip(name)}> - <div className='catalog-tile-vendor-name'>{name}</div> - </OverlayTrigger> - </div> +const CatalogTileVendorName = ({vendorName, catalogItemTypeClass}) => { + const name = catalogItemTypeClass === catalogItemTypeClasses.SOFTWARE_PRODUCT ? vendorName : ''; + return ( name ? + <OverlayTrigger placement='top' overlay={tooltip(name)}> + <div className='catalog-tile-vendor-name'>{name}</div> + </OverlayTrigger> : <div className='catalog-tile-vendor-name'>{name}</div> ); }; const CatalogTileItemName = ({name}) => ( - <div> - <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> - <div className='catalog-tile-item-name'>{name}</div> - </OverlayTrigger> - </div> + <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> + <div className='catalog-tile-item-name'>{name}</div> + </OverlayTrigger> ); const VersionInfo = ({version}) => ( @@ -64,7 +59,7 @@ const VersionInfo = ({version}) => ( <div className='catalog-tile-item-version' data-test-id='catalog-item-version'> V {version} </div> - </div> + </div> ); const EntityDetails = ({catalogItemData, catalogItemTypeClass}) => { @@ -73,54 +68,55 @@ const EntityDetails = ({catalogItemData, catalogItemTypeClass}) => { <div className='catalog-tile-entity-details'> <CatalogTileVendorName catalogItemTypeClass={catalogItemTypeClass} vendorName={vendorName}/> <CatalogTileItemName name={name}/> - <VersionInfo version={version.label} /> - </div> + <VersionInfo version={version.label}/> + </div> ); }; -const ItemStatusInfo = ({catalogItemTypeClass, lockingUser, itemStatus}) => { +const ItemStatusInfo = ({catalogItemTypeClass, lockingUser, itemStatus}) => { const status = statusBarTextMap[itemStatus]; const lockedBy = lockingUser ? ` by ${lockingUser}` : ''; const toolTipMsg = `${status}${lockedBy}`; return ( - <div className={'catalog-tile-content ' + catalogItemTypeClass}> + <div className={'catalog-tile-content ' + catalogItemTypeClass}> <div className='catalog-tile-locking-user-name'>{i18n(status)}</div> - <OverlayTrigger placement='top' overlay={tooltip(toolTipMsg)}> + <OverlayTrigger placement='top' overlay={tooltip(toolTipMsg)}> <div className='catalog-tile-check-in-status'><SVGIcon name={itemStatus === statusEnum.CHECK_OUT_STATUS ? 'unlocked' : 'locked'} data-test-id={itemStatus === statusEnum.CHECK_IN_STATUS ? 'catalog-item-checked-in' : 'catalog-item-checked-out'}/> </div> - </OverlayTrigger> + </OverlayTrigger> </div> - + ); }; -const CatalogItemDetails = ({catalogItemData, catalogItemTypeClass, onSelect, onMigrate}) => { - +const CatalogItemDetails = ({catalogItemData, catalogItemTypeClass, onSelect, onMigrate}) => { + let {status: itemStatus} = VersionControllerUtils.getCheckOutStatusKindByUserID(catalogItemData.status, catalogItemData.lockingUser); - + return ( <CatalogTile catalogItemTypeClass={catalogItemTypeClass} onSelect={() => { if (catalogItemData.isOldVersion && catalogItemData.isOldVersion === migrationStatusMapper.OLD_VERSION) { onMigrate({ softwareProduct: catalogItemData }); - } else { + } + else { onSelect(); } }} data-test-id={catalogItemTypeClass}> - <div className='catalog-tile-top item-details'> - <ItemTypeTitle catalogItemTypeClass={catalogItemTypeClass}/> - <CatalogTileIcon catalogItemTypeClass={catalogItemTypeClass}/> - <EntityDetails catalogItemTypeClass={catalogItemTypeClass} catalogItemData={catalogItemData} /> - <ItemStatusInfo itemStatus={itemStatus} catalogItemTypeClass={catalogItemTypeClass} lockingUser={catalogItemData.lockingUser} /> + <div className='catalog-tile-top item-details'> + <ItemTypeTitle catalogItemTypeClass={catalogItemTypeClass}/> + <CatalogTileIcon catalogItemTypeClass={catalogItemTypeClass}/> + <EntityDetails catalogItemTypeClass={catalogItemTypeClass} catalogItemData={catalogItemData}/> + <ItemStatusInfo itemStatus={itemStatus} catalogItemTypeClass={catalogItemTypeClass} lockingUser={catalogItemData.lockingUser}/> </div> </CatalogTile> ); - + }; CatalogItemDetails.PropTypes = { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx index 17248e3b02..51702e6d36 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx @@ -15,7 +15,7 @@ */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; const SoftwareProductListHeader = ({selectedVendor, onBack}) => ( <div className='vendor-page-header'> diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js index 73a447558d..0d1e3992ce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js @@ -26,7 +26,7 @@ import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/Soft function getMessageForMigration(name) { return ( <div> - <div>{i18n('{name} needs to be updated. Click ‘Checkout & Update’, to proceed.',{name})}</div> + <div>{i18n(`${name} needs to be updated. Click ‘Checkout & Update’, to proceed.`)}</div> <div>{i18n('Please don’t forget to submit afterwards')}</div> </div> ); @@ -65,7 +65,7 @@ const OnboardingCatalogActionHelper = { type: modalActionTypes.GLOBAL_MODAL_WARNING, data: { title: 'WARNING', - msg: i18n('{name} is locked by user {lockingUser} for self-healing',{name, lockingUser}) + msg: i18n(`${name} is locked by user ${lockingUser} for self-healing`) } }); } else { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx index cecccdd9ad..d3d6f9ce37 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx @@ -19,7 +19,7 @@ import CatalogTile from '../CatalogTile.jsx'; import classnames from 'classnames'; import VSPOverlay from './VSPOverlay.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import tooltip from './Tooltip.jsx'; @@ -50,7 +50,7 @@ class VendorItem extends React.Component { </div> <OverlayTrigger placement='top' overlay={tooltip(vendorName)}> <div className='catalog-tile-item-name'>{vendorName}</div> - </OverlayTrigger> + </OverlayTrigger> <div className={classnames('catalog-tile-vsp-count', {active: shouldShowOverlay}, {clickable: softwareProductList.length})} onClick={(event) => this.handleVspCountClick(event)} @@ -63,7 +63,7 @@ class VendorItem extends React.Component { </div> </div> </div> - + {shouldShowOverlay && softwareProductList.length > 0 && <VSPOverlay onMigrate={onMigrate} VSPList={softwareProductList} onSelectVSP={onSelectVSP} onSeeMore={() => onVendorSelect(vendor)}/>} </CatalogTile> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js index 12f68a2afe..07d6c740e0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -22,7 +22,7 @@ import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; -import {navigationItems, mapScreenToNavigationItem} from './SoftwareProductConstants.js'; +import {navigationItems, mapScreenToNavigationItem, onboardingMethod as onboardingMethodTypes} from './SoftwareProductConstants.js'; import SoftwareProductActionHelper from './SoftwareProductActionHelper.js'; import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductDependenciesActionHelper from './dependencies/SoftwareProductDependenciesActionHelper.js'; @@ -36,7 +36,7 @@ function getActiveNavigationId(screen, componentId) { return activeItemId; } -const buildComponentNavigationBarGroups = ({componentId, meta}) => { +const buildComponentNavigationBarGroups = ({componentId, meta, hasImages}) => { const groups = ([ { id: navigationItems.GENERAL + '|' + componentId, @@ -64,6 +64,12 @@ const buildComponentNavigationBarGroups = ({componentId, meta}) => { disabled: false, meta }, { + id: navigationItems.IMAGES + '|' + componentId, + name: i18n('Images'), + disabled: false, + hidden: (!hasImages), + meta + }, { id: navigationItems.PROCESS_DETAILS + '|' + componentId, name: i18n('Process Details'), disabled: false, @@ -79,9 +85,9 @@ const buildComponentNavigationBarGroups = ({componentId, meta}) => { return groups; }; -const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) => { +const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds, imagesNavigationList}) => { const {softwareProductEditor: {data: currentSoftwareProduct = {}}} = softwareProduct; - const {id, name} = currentSoftwareProduct; + const {id, name, onboardingMethod} = currentSoftwareProduct; const groups = [{ id: id, name: name, @@ -96,6 +102,13 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co name: i18n('General'), disabled: false, meta + }, + { + id: navigationItems.DEPLOYMENT_FLAVORS, + name: i18n('Deployment Flavors'), + disabled: false, + hidden: onboardingMethod !== onboardingMethodTypes.MANUAL, + meta }, { id: navigationItems.PROCESS_DETAILS, name: i18n('Process Details'), @@ -135,7 +148,8 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co name: displayName, meta, expanded: mapOfExpandedIds[navigationItems.COMPONENTS + '|' + id] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, - items: buildComponentNavigationBarGroups({componentId: id, meta}) + items: buildComponentNavigationBarGroups({componentId: id, meta, + hasImages : (onboardingMethod === onboardingMethodTypes.MANUAL || imagesNavigationList[id] === true)}) })) ] } @@ -179,17 +193,18 @@ function buildMeta({softwareProduct, componentId, softwareProductDependencies}) const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { const {softwareProductEditor, softwareProductComponents, softwareProductDependencies} = softwareProduct; const {mapOfExpandedIds = []} = softwareProductEditor; - const {componentsList = []} = softwareProductComponents; + const {componentsList = [], images: {imagesNavigationList}} = softwareProductComponents; + const meta = buildMeta({softwareProduct, componentId, softwareProductDependencies}); return { versionControllerProps: buildVersionControllerProps(softwareProduct), - navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}), + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds, imagesNavigationList}), meta }; }; -const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, - meta: {isReadOnlyMode, softwareProduct, version, qdata, softwareProductDependencies, +const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, + meta: {isReadOnlyMode, softwareProduct, version, qdata, softwareProductDependencies, currentComponentMeta: {componentData, componentQdata}}}) => { let promise; if (isReadOnlyMode) { @@ -208,6 +223,7 @@ const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentI case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata: componentQdata}); break; @@ -242,6 +258,9 @@ const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentC case navigationItems.NETWORKS: OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; + case navigationItems.IMAGES: + OnboardingActionHelper.navigateToComponentImages(dispatch, {softwareProductId, componentId: nextComponentId, version}); + break; case navigationItems.STORAGE: OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; @@ -266,7 +285,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr let {heatSetup, heatSetupCache} = meta; let heatSetupPopupPromise = screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS ? HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : - Promise.resolve(); + Promise.resolve(); let preNavigate = meta ? autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}) : Promise.resolve(); version = version || (meta ? meta.version : undefined); Promise.all([preNavigate, heatSetupPopupPromise]).then(() => { @@ -277,6 +296,9 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr case navigationItems.GENERAL: OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId, version}); break; + case navigationItems.DEPLOYMENT_FLAVORS: + OnboardingActionHelper.navigateToSoftwareProductDeployment(dispatch, {softwareProductId, version}); + break; case navigationItems.PROCESS_DETAILS: OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}); break; @@ -299,7 +321,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr onComponentNavigate(dispatch, {id, softwareProductId, version, screen, currentComponentId}); break; } - }).catch(() => {}); + }).catch((e) => {console.error(e);}); } }; @@ -311,6 +333,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: props.onSave = () => { @@ -335,7 +358,7 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version: newVersion}); } }); - }).catch(() => {}); + }).catch((e) => {console.error(e);}); }; return props; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js index 6f53886350..d6ba86ad6e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -27,6 +27,7 @@ import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel import {actionTypes as HeatSetupActions} from 'sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js'; import {actionTypes as featureGroupsActionConstants} from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js'; import {actionTypes as licenseAgreementActionTypes} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js'; +import {actionTypes as componentActionTypes} from './components/SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; @@ -57,7 +58,8 @@ function putSoftwareProduct(softwareProduct) { vendorName: softwareProduct.vendorName, licensingVersion: softwareProduct.licensingVersion && softwareProduct.licensingVersion.id ? softwareProduct.licensingVersion : {} , icon: softwareProduct.icon, - licensingData: softwareProduct.licensingData + licensingData: softwareProduct.licensingData, + onboardingMethod: softwareProduct.onboardingMethod }); } @@ -249,7 +251,8 @@ const SoftwareProductActionHelper = { processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){ return validateHeatCandidate(softwareProductId, version).then(response => { if (response.status === 'Success') { - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + let isFetchImageDetails = true; + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails}); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); } }); @@ -459,8 +462,20 @@ const SoftwareProductActionHelper = { }, /** for the next verision */ - addComponent(dispatch) { - return dispatch; + addComponent(dispatch, {softwareProductId, modalClassName}) { + SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch); + dispatch({ + type: componentActionTypes.COMPONENT_CREATE_OPEN + }); + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.COMPONENT_CREATION, + modalComponentProps: {softwareProductId}, + modalClassName, + title: 'Create Virtual Function Component' + } + }); }, migrateSoftwareProduct(dispatch, {softwareProduct}) { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js index f29b0f6e0d..0379ee5d4a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js @@ -38,11 +38,13 @@ export const navigationItems = keyMirror({ VENDOR_SOFTWARE_PRODUCT: 'vendor-software-product', GENERAL: 'general', PROCESS_DETAILS: 'process-details', - NETWORKS: 'networks', - DEPENDENCIES: 'dependencies', + DEPLOYMENT_FLAVORS: 'deployment-flavor', + NETWORKS: 'networks', + IMAGES: 'images', ATTACHMENTS: 'attachments', ACTIVITY_LOG: 'activity-log', COMPONENTS: 'components', + DEPENDENCIES: 'dependencies', COMPUTE: 'compute', LOAD_BALANCING: 'load-balancing', @@ -50,6 +52,11 @@ export const navigationItems = keyMirror({ MONITORING: 'monitoring' }); +export const onboardingMethod = { + MANUAL: 'Manual', + HEAT: 'HEAT' +}; + export const forms = keyMirror({ VENDOR_SOFTWARE_PRODUCT_DETAILS: 'vendor-software-product-details', }); @@ -61,6 +68,7 @@ export const mapScreenToNavigationItem = { [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: navigationItems.GENERAL, [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: navigationItems.ATTACHMENTS, [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT]: navigationItems.DEPLOYMENT_FLAVORS, [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS, [enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG]: navigationItems.ACTIVITY_LOG, [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: navigationItems.DEPENDENCIES, @@ -69,6 +77,7 @@ export const mapScreenToNavigationItem = { [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: navigationItems.COMPUTE, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: navigationItems.LOAD_BALANCING, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES]: navigationItems.IMAGES, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS, [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js index 97988d87f9..5248c4e8fd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -22,6 +22,8 @@ import SoftwareProductCreationReducer from './creation/SoftwareProductCreationRe import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js'; import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js'; import SoftwareProductProcessesEditorReducer from './processes/SoftwareProductProcessesEditorReducer.js'; +import SoftwareProductDeploymentListReducer from './deployment/SoftwareProductDeploymentListReducer.js'; +import SoftwareProductDeploymentEditorReducer from './deployment/editor/SoftwareProductDeploymentEditorReducer.js'; import SoftwareProductNetworksListReducer from './networks/SoftwareProductNetworksListReducer.js'; import SoftwareProductComponentsListReducer from './components/SoftwareProductComponentsListReducer.js'; import SoftwareProductComponentEditorReducer from './components/SoftwareProductComponentEditorReducer.js'; @@ -31,12 +33,19 @@ import SoftwareProductComponentProcessesEditorReducer from './components/process import {actionTypes as componentProcessesActionTypes} from './components/processes/SoftwareProductComponentProcessesConstants.js'; import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js'; import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; +import SoftwareProductComponentsImageListReducer from './components/images/SoftwareProductComponentsImageListReducer.js'; +import SoftwareProductComponentsImageEditorReducer from './components/images/SoftwareProductComponentsImageEditorReducer.js'; +import SoftwareProductComponentsImageNavigationReducer from './components/images/SoftwareProductComponentsImageNavigationReducer.js'; +import SoftwareProductComponentsNICCreationReducer from './components/network/NICCreation/NICCreationReducer.js'; import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; +import SoftwareProductComponentsComputeFlavorListReducer from './components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js'; +import SoftwareProductComponentsComputeFlavorReducer from './components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js'; import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; import SoftwareProductDependenciesReducer from './dependencies/SoftwareProductDependenciesReducer.js'; import {createJSONSchemaReducer, createComposedJSONSchemaReducer} from 'sdc-app/common/reducers/JSONSchemaReducer.js'; -import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {COMPONENTS_QUESTIONNAIRE, COMPONENTS_COMPUTE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; +import {IMAGE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js'; export default combineReducers({ softwareProductAttachments: combineReducers({ @@ -51,6 +60,10 @@ export default combineReducers({ processesEditor: createPlainDataReducer(SoftwareProductProcessesEditorReducer), processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state }), + softwareProductDeployment: combineReducers({ + deploymentFlavors: SoftwareProductDeploymentListReducer, + deploymentFlavorEditor: createPlainDataReducer(SoftwareProductDeploymentEditorReducer) + }), softwareProductNetworks: combineReducers({ networksList: SoftwareProductNetworksListReducer }), @@ -65,7 +78,17 @@ export default combineReducers({ }), network: combineReducers({ nicList: SoftwareProductComponentsNICListReducer, - nicEditor: createPlainDataReducer(createComposedJSONSchemaReducer(NIC_QUESTIONNAIRE, SoftwareProductComponentsNICEditorReducer)) + nicEditor: createPlainDataReducer(createComposedJSONSchemaReducer(NIC_QUESTIONNAIRE, SoftwareProductComponentsNICEditorReducer)), + nicCreation: createPlainDataReducer(SoftwareProductComponentsNICCreationReducer) + }), + images: combineReducers({ + imagesList: SoftwareProductComponentsImageListReducer, + imagesNavigationList: SoftwareProductComponentsImageNavigationReducer, + imageEditor: createPlainDataReducer(createComposedJSONSchemaReducer(IMAGE_QUESTIONNAIRE, SoftwareProductComponentsImageEditorReducer)) + }), + computeFlavor: combineReducers({ + computesList: SoftwareProductComponentsComputeFlavorListReducer, + computeEditor: createPlainDataReducer(createComposedJSONSchemaReducer(COMPONENTS_COMPUTE_QUESTIONNAIRE, SoftwareProductComponentsComputeFlavorReducer)), }), monitoring: SoftwareProductComponentsMonitoringReducer }), diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx index 0d8bc58361..901a583e24 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx @@ -14,14 +14,14 @@ * permissions and limitations under the License. */ import React, {Component} from 'react'; -import Button from 'react-bootstrap/lib/Button.js'; +import Button from 'sdc-ui/lib/react/Button.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import FormControl from 'react-bootstrap/lib/FormControl.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import SelectInput from 'nfvo-components/input/SelectInput.jsx'; import Icon from 'nfvo-components/icon/Icon.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import {fileTypes} from './HeatSetupConstants.js'; import {tabsMapping} from '../SoftwareProductAttachmentsConstants.js'; import {sortable} from 'react-sortable'; @@ -76,8 +76,8 @@ class SortableModuleFileList extends Component { <div className='modules-list-wrapper'> <div className='modules-list-header'> <div className='modules-list-controllers'> - {!isBaseExist && <Button bsStyle='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} - <Button bsStyle='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> + {!isBaseExist && <Button btnType='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} + <Button btnType='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> </div> </div> <ul>{listItems}</ul> @@ -102,7 +102,7 @@ const EmptyListContent = props => { return ( <div className='go-to-validation-button-wrapper'> <div className='all-files-assigned'>{i18n(displayText)}</div> - {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angle-right'/></div>} + {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angleRight'/></div>} </div> ); }; @@ -212,7 +212,7 @@ class ModuleFile extends Component { data-test-id={isBase ? 'base-name' : 'module-name'}/>} </div> </div> - <SVGIcon name='trash-o' onClick={() => onModuleDelete(name)} data-test-id='module-delete'/> + <SVGIcon name='trashO' onClick={() => onModuleDelete(name)} data-test-id='module-delete'/> </div> <div className='modules-list-item-selectors'> <SelectWithFileType diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx index 25ad90f351..f2d5de4dff 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx @@ -17,7 +17,7 @@ import React, {Component, PropTypes} from 'react'; import classNames from 'classnames'; import Collapse from 'react-bootstrap/lib/Collapse.js'; import Icon from 'nfvo-components/icon/Icon.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js'; @@ -68,7 +68,7 @@ function HeatFileTreeRow(props) { isFolder && <div onClick={() => toggleExpanded(path)} className='tree-node-expander'> - <SVGIcon name={!node.expanded ? 'chevron-up' : 'chevron-down'} data-test-id='validation-tree-block-toggle'/> + <SVGIcon name={!node.expanded ? 'chevronUp' : 'chevronDown'} data-test-id='validation-tree-block-toggle'/> </div> } { @@ -205,23 +205,19 @@ class HeatMessageBoard extends Component { key={error.name + error.errorMessage + error.parentName + rand} className='error-item' data-test-id='validation-error'> {error.level === errorLevels.WARNING ? - <SVGIcon name='exclamation-triangle-line' iconClassName='large' /> : <Icon image='error-lg' /> } + <SVGIcon name='exclamationTriangleLine' iconClassName='large' /> : <Icon image='error-lg' /> } <span className='error-item-file-type'> { (this.props.selectedNode === nodeFilters.ALL) ? <span> <span className='error-file-name'> - {i18n('{errorName}:', { - errorName: error.name - })} + {i18n(`${error.name}`)} </span> <span> - {i18n('{message}', {message: error.errorMessage})} + {i18n(error.errorMessage)} </span> </span> : - i18n('{errorMsg}', { - errorMsg: error.errorMessage - }) + i18n(error.errorMesage) } </span> </div> @@ -249,7 +245,7 @@ class ErrorsAndWarningsCount extends Component { <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>{errors.errorCount}</div> </div>} {(errors.warningCount > 0) && <div className='counter'> - <SVGIcon name='exclamation-triangle-line' iconClassName={size} /> + <SVGIcon name='exclamationTriangleLine' iconClassName={size} /> <div className={'warning-text ' + (size ? size : '')} data-test-id='validation-warning-count'>{errors.warningCount}</div> </div>} </div>); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js index 41e7556749..b13bde03c8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js @@ -17,6 +17,24 @@ import {actionTypes, forms} from './SoftwareProductComponentsConstants.js'; export default (state = {}, action) => { switch (action.type) { + case actionTypes.COMPONENT_CREATE_OPEN: + return { + ...state, + formName: forms.CREATE_FORM, + formReady: null, + genericFieldInfo: { + 'displayName' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'validateName', data: true}, {type: 'maxLength', data: 25}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + } + } + }; case actionTypes.COMPONENT_LOAD: return { ...state, @@ -34,6 +52,11 @@ export default (state = {}, action) => { errorText: '', validations: [] }, + 'nfcFunction' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 30}] + }, 'description' : { isValid: true, errorText: '', @@ -41,6 +64,27 @@ export default (state = {}, action) => { } } }; + case actionTypes.COMPONENT_UPDATE: + return { + ...state, + data: action.component + }; + case actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE: + return { + ...state, + qdata: action.payload.qdata || state.qdata, + qschema: action.payload.qschema || state.qschema + }; + case actionTypes.COMPONENT_DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.COMPONENT_DATA_CLEAR: + return {}; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js new file mode 100644 index 0000000000..61aebdf293 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js @@ -0,0 +1,65 @@ +import {connect} from 'react-redux'; +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import SoftwareProductComponentsList from './SoftwareProductComponentsList.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductComponentsActionHelper from '../components/SoftwareProductComponentsActionHelper.js'; +import {onboardingMethod} from '../SoftwareProductConstants.js'; +import ConfirmationModalConstants from 'nfvo-components/modal/GlobalModalConstants.js'; + +const generateMessage = (name) => { + return i18n(`Are you sure you want to delete ${name}?`); +}; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductComponents} = softwareProduct; + let {componentsList} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isReadOnlyMode, + componentsList, + isManual: currentSoftwareProduct.onboardingMethod === onboardingMethod.MANUAL + + }; +}; + +class SoftwareProductComponentsView extends React.Component { + render() { + let {currentSoftwareProduct, isReadOnlyMode, componentsList, isManual, onDeleteComponent} = this.props; + return ( + <SoftwareProductComponentsList + isReadOnlyMode={isReadOnlyMode} + componentsList={componentsList} + onDeleteComponent={onDeleteComponent} + isManual={isManual} + currentSoftwareProduct={currentSoftwareProduct}/>); + } + +} + +const mapActionToProps = (dispatch) => { + return { + onComponentSelect: ({id: softwareProductId, componentId, version}) => { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); + }, + onAddComponent: (softwareProductId) => SoftwareProductComponentsActionHelper.addComponent(dispatch, {softwareProductId}), + onDeleteComponent: (component, softwareProductId, version) => dispatch({ + type: ConfirmationModalConstants.GLOBAL_MODAL_WARNING, + data:{ + msg: generateMessage(component.displayName), + onConfirmed: ()=>SoftwareProductComponentsActionHelper.deleteComponent(dispatch, + { + softwareProductId, + componentId: component.id, + version + }) + } + }) + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js index 4e526d3b56..71dc8325ad 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -18,6 +18,8 @@ import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes, COMPONENTS_QUESTIONNAIRE} from './SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductComponentsImageActionHelper from './images/SoftwareProductComponentsImageActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; function baseUrl(softwareProductId, version) { const versionId = version.id; @@ -46,17 +48,53 @@ function putSoftwareProductComponent(softwareProductId, version, vspComponentId, name: vspComponent.name, displayName: vspComponent.displayName, vfcCode: vspComponent.vfcCode, + nfcFunction: vspComponent.nfcFunction, description: vspComponent.description }); } +function deleteSoftwareProductComponent(softwareProductId, componentId, version) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version)}/${componentId}`,); +} + + +function postSoftwareProductComponent(softwareProductId, vspComponent, version) { + + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, { + name: vspComponent.displayName, + displayName: vspComponent.displayName, + description: vspComponent.description + }); +} + + const SoftwareProductComponentsActionHelper = { - fetchSoftwareProductComponents(dispatch, {softwareProductId, version}) { + fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails = false}) { return fetchSoftwareProductComponents(softwareProductId, version).then(response => { - dispatch({ - type: actionTypes.COMPONENTS_LIST_UPDATE, - componentsList: response.results - }); + let componentImagesCalls = []; + if (isFetchImageDetails && response.listCount) { + response.results.map(component => { + let componentId = component.id; + componentImagesCalls[componentImagesCalls.length] = + SoftwareProductComponentsImageActionHelper.fetchImagesList(dispatch, { + softwareProductId, + componentId, + version + }); + + }); + return Promise.all(componentImagesCalls).then(() => { + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); + }); + } else { + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); + } }); }, @@ -110,7 +148,45 @@ const SoftwareProductComponentsActionHelper = { type: actionTypes.COMPONENTS_LIST_UPDATE, componentsList: [] }); - } + }, + + createSoftwareProductComponent(dispatch,{softwareProductId, componentData, version}) { + SoftwareProductComponentsActionHelper.closeComponentCreationModal(dispatch); + /* for mock only */ + + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: [{id: '123', ...componentData}] + }); + + postSoftwareProductComponent(softwareProductId, componentData, version).then(() => { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + }); + }, + + clearComponentCreationData(dispatch) { + dispatch({ + type: actionTypes.COMPONENT_DATA_CLEAR + }); + }, + + closeComponentCreationModal(dispatch) { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }); + SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch); + }, + + deleteComponent(dispatch, {softwareProductId, componentId, version}) { + deleteSoftwareProductComponent(softwareProductId, componentId, version); + dispatch({ + type: actionTypes.COMPONENT_DELETE, + componentId: componentId + }); + }, + + + }; export default SoftwareProductComponentsActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js index 9307b099ed..35633b65cf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js @@ -18,7 +18,13 @@ import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ COMPONENTS_LIST_UPDATE: null, COMPONENTS_LIST_EDIT: null, - COMPONENT_LOAD: null + COMPONENT_UPDATE: null, + COMPONENT_DATA_CHANGED: null, + COMPONENT_DATA_CLEAR: null, + COMPONENT_QUESTIONNAIRE_UPDATE: null, + COMPONENT_DELETE: null, + COMPONENT_LOAD: null, + COMPONENT_CREATE_OPEN: null }); export const storageConstants = keyMirror({ @@ -30,16 +36,20 @@ export const storageConstants = keyMirror({ export const forms = keyMirror({ ALL_SPC_FORMS: null, - NIC_EDIT_FORM: null + NIC_EDIT_FORM: null, + CREATE_FORM: null, + IMAGE_EDIT_FORM: null }); export const COMPONENTS_QUESTIONNAIRE = 'component'; +export const COMPONENTS_COMPUTE_QUESTIONNAIRE = 'compute'; export const navigationItems = keyMirror({ STORAGE: 'Storage', PROCESS_DETAILS: 'Process Details', MONITORING: 'Monitoring', NETWORK: 'Network', + IMAGES: 'Images', COMPUTE: 'Compute', LOAD_BALANCING: 'High Availability & Load Balancing' }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js index f789a92c6f..bd4c2fa884 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js @@ -14,21 +14,16 @@ * permissions and limitations under the License. */ import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + import SoftwareProductComponentsListView from './SoftwareProductComponentsListView.jsx'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; -import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; - - -const mapStateToProps = ({softwareProduct}) => { - let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductComponents} = softwareProduct; - let {componentsList} = softwareProductComponents; - let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import SoftwareProductComponentsActionHelper from '../components/SoftwareProductComponentsActionHelper.js'; +import {actionTypes as globalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; - return { - currentSoftwareProduct, - isReadOnlyMode, - componentsList - }; +const generateMessage = (name) => { + return i18n(`Are you sure you want to delete ${name}?`); }; @@ -36,8 +31,21 @@ const mapActionToProps = (dispatch) => { return { onComponentSelect: ({id: softwareProductId, componentId, version}) => { OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); - } + }, + onAddComponent: (softwareProductId) => SoftwareProductActionHelper.addComponent(dispatch, {softwareProductId, modalClassName: 'create-vfc-modal'}), + onDeleteComponent: (component, softwareProductId, version) => dispatch({ + type: globalModalActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateMessage(component.displayName), + onConfirmed: ()=>SoftwareProductComponentsActionHelper.deleteComponent(dispatch, + { + softwareProductId, + componentId: component.id, + version + }) + } + }) }; }; -export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsListView); +export default connect(null, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js index c7aaca5573..92211e0fd2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js @@ -22,6 +22,8 @@ export default (state = [], action) => { case actionTypes.COMPONENTS_LIST_EDIT: const indexForEdit = state.findIndex(component => component.id === action.component.id); return [...state.slice(0, indexForEdit), action.component, ...state.slice(indexForEdit + 1)]; + case actionTypes.COMPONENT_DELETE: + return state.filter(component => component.id !== action.componentId); default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx index c28831fbde..a2a1964299 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx @@ -40,11 +40,11 @@ class SoftwareProductComponentsListView extends React.Component { }; render() { - let {componentsList = []} = this.props; + let {componentsList = [], isManual} = this.props; return ( <div className=''> { - componentsList.length > 0 && this.renderComponents() + (componentsList.length > 0 || isManual) && this.renderComponents() } </div> ); @@ -52,15 +52,16 @@ class SoftwareProductComponentsListView extends React.Component { renderComponents() { const {localFilter} = this.state; - let {isReadOnlyMode} = this.props; - + const {isManual, onAddComponent, isReadOnlyMode, currentSoftwareProduct: {id: softwareProductId}, componentsList } = this.props; return ( <ListEditorView title={i18n('Virtual Function Components')} filterValue={localFilter} placeholder={i18n('Filter Components')} onFilter={value => this.setState({localFilter: value})} - isReadOnlyMode={isReadOnlyMode} + isReadOnlyMode={isReadOnlyMode || !!this.filterList().length} + plusButtonTitle={i18n('Add Component')} + onAdd={isManual && componentsList.length === 0 ? () => onAddComponent(softwareProductId) : false} twoColumns> {this.filterList().map(component => this.renderComponentsListItem(component))} </ListEditorView> @@ -69,11 +70,12 @@ class SoftwareProductComponentsListView extends React.Component { renderComponentsListItem(component) { let {id: componentId, name, displayName, description = ''} = component; - let {currentSoftwareProduct: {id, version}, onComponentSelect} = this.props; + let {currentSoftwareProduct: {id, version}, onComponentSelect, isManual, isReadOnlyMode, onDeleteComponent} = this.props; return ( <ListEditorItemView key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} className='list-editor-item-view' + onDelete={isManual && !isReadOnlyMode ? () => onDeleteComponent(component, id, version) : false} onSelect={() => onComponentSelect({id, componentId, version})}> <ListEditorItemViewField> <div className='name'>{displayName}</div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js new file mode 100644 index 0000000000..02c09fbdf8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js @@ -0,0 +1,169 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes} from './computeComponents/computeFlavor/ComputeFlavorConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes as globalModalActionTypes, modalSizes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {COMPONENTS_COMPUTE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; + +function baseUrl(softwareProductId, componentId, version) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/compute-flavors`; +} + +function baseUrlVSPLevel(softwareProductId, version){ + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/compute-flavors`; +} + +function fetchComputesList(softwareProductId, componentId, version){ + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId, version)}`); +} + +function fetchComputesListForVSP(softwareProductId, version){ + return RestAPIUtil.fetch(`${baseUrlVSPLevel(softwareProductId, version)}`); +} + +function fetchCompute(softwareProductId, componentId, computeId, version) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId, version)}/${computeId}`); +} + +function fetchComputeQuestionnaire({softwareProductId, componentId, computeId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId, version)}/${computeId}/questionnaire`); +} + +function postCompute({softwareProductId, componentId, compute, version}) { + return RestAPIUtil.post(baseUrl(softwareProductId, componentId, version), compute); +} + +function putCompute({softwareProductId, componentId, compute, version}) { + const computeData = { + name: compute.name, + description: compute.description + }; + return RestAPIUtil.put(`${baseUrl(softwareProductId, componentId, version)}/${compute.id}`, computeData); +} + +function putComputeQuestionnaire({softwareProductId, componentId, computeId, qdata, version}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, componentId, version)}/${computeId}/questionnaire`, qdata); +} + +function deleteCompute({softwareProductId, componentId, computeId, version}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId, version)}/${computeId}`); +} + + +const ComputeFlavorActionHelper = { + openComputeEditor(dispatch, {props}) { + dispatch({ + type: actionTypes.computeEditor.LOAD_EDITOR_DATA, + compute: props.compute || {} + }); + dispatch({ + type: globalModalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.COMPONENT_COMPUTE_FLAVOR_EDITOR, + modalClassName: `compute-flavor-editor-modal-${props.compute ? 'edit' : 'create'}`, + modalComponentProps: {...props, size: props.compute ? modalSizes.LARGE : undefined, dialogClassName:'compute-flavor-editor-modal'}, + title: `${props.compute ? i18n('Edit Compute Flavor') : i18n('Create New Compute Flavor')}` + } + }); + }, + + closeComputeEditor(dispatch){ + dispatch({ + type: globalModalActionTypes.GLOBAL_MODAL_CLOSE + }); + dispatch({ + type: actionTypes.computeEditor.CLEAR_DATA + }); + }, + + fetchComputesList(dispatch, {softwareProductId, componentId, version}) { + return fetchComputesList(softwareProductId, componentId, version).then(response => dispatch({ + type: actionTypes.COMPUTE_FLAVORS_LIST_LOADED, + response + })); + }, + + fetchComputesListForVSP(dispatch, {softwareProductId, version}) { + return fetchComputesListForVSP(softwareProductId, version).then(response => dispatch({ + type: actionTypes.COMPUTE_FLAVORS_LIST_LOADED, + response + })); + }, + + loadComputeData({softwareProductId, componentId, computeId, version}) { + return fetchCompute(softwareProductId, componentId, computeId, version); + }, + + loadComputeQuestionnaire(dispatch, {softwareProductId, componentId, computeId, version}) { + return fetchComputeQuestionnaire({softwareProductId, componentId, computeId, version}).then(response => + ValidationHelper.qDataLoaded(dispatch, {qName: COMPONENTS_COMPUTE_QUESTIONNAIRE ,response: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + }}) + ); + }, + + loadCompute(dispatch, {softwareProductId, componentId, version, computeId, isReadOnlyMode}){ + return ComputeFlavorActionHelper.loadComputeData({softwareProductId, componentId, computeId, version}).then(({data}) => + ComputeFlavorActionHelper.loadComputeQuestionnaire(dispatch, {softwareProductId, componentId, computeId, version}).then(() => + ComputeFlavorActionHelper.openComputeEditor(dispatch, {props: {softwareProductId, componentId, version, isReadOnlyMode, compute: {id: computeId, ...data}}}) + )); + }, + + saveComputeDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data: compute, qdata, version}) { + ComputeFlavorActionHelper.closeComputeEditor(dispatch); + if(compute.id) { + return Promise.all([ + putComputeQuestionnaire({softwareProductId, componentId, computeId: compute.id, qdata, version}), + putCompute({softwareProductId, componentId, compute, version}).then(() => { + dispatch({ + type: actionTypes.COMPUTE_LIST_EDIT, + compute + }); + }) + ]); + } + else { + return postCompute({softwareProductId, componentId, compute, version}).then(response => + dispatch({ + type: actionTypes.ADD_COMPUTE, + compute: { + ...compute, + id: response.id, + componentId + } + }) + ); + } + }, + + deleteCompute(dispatch, {softwareProductId, componentId, computeId, version}) { + return deleteCompute({softwareProductId, componentId, computeId, version}).then(() => dispatch({ + type: actionTypes.DELETE_COMPUTE, + computeId + })); + } +}; + +export default ComputeFlavorActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js index e97477b54d..bb8df29b82 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js @@ -19,18 +19,23 @@ import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwarePr import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {onboardingMethod} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -const mapStateToProps = ({softwareProduct}) => { +const mapStateToProps = ({softwareProduct, currentScreen: {props: {softwareProductId, componentId}}}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; + let {componentEditor: {qdata, dataMap, qgenericFieldInfo}, computeFlavor: {computesList: computeFlavorsList}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { qdata, dataMap, qgenericFieldInfo, - isReadOnlyMode + isReadOnlyMode, + softwareProductId, + componentId, + computeFlavorsList, + isManual: currentVSP.onboardingMethod === onboardingMethod.MANUAL }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx index 8c197f0d49..dd524a35f3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx @@ -15,9 +15,9 @@ */ import React from 'react'; import Form from 'nfvo-components/input/validation/Form.jsx'; -import VmSizing from './computeComponents/VmSizing.jsx'; import NumberOfVms from './computeComponents/NumberOfVms.jsx'; import GuestOs from './computeComponents/GuestOs.jsx'; +import ComputeFlavors from './computeComponents/ComputeFlavors.js'; import Validator from 'nfvo-utils/Validator.js'; class SoftwareProductComponentComputeView extends React.Component { @@ -26,13 +26,15 @@ class SoftwareProductComponentComputeView extends React.Component { dataMap: React.PropTypes.object, qgenericFieldInfo: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, + isManual: React.PropTypes.bool, onQDataChanged: React.PropTypes.func.isRequired, qValidateData: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired }; render() { - let {qdata, dataMap, qgenericFieldInfo, isReadOnlyMode, onQDataChanged, qValidateData, onSubmit} = this.props; + let {softwareProductId, componentId, version, qdata, dataMap, qgenericFieldInfo, isReadOnlyMode, onQDataChanged, qValidateData, + onSubmit, computeFlavorsList, isManual} = this.props; return ( <div className='vsp-component-questionnaire-view'> @@ -44,11 +46,12 @@ class SoftwareProductComponentComputeView extends React.Component { onSubmit={() => onSubmit({qdata})} className='component-questionnaire-validation-form' isReadOnlyMode={isReadOnlyMode} > - <VmSizing onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> <NumberOfVms onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} qValidateData={qValidateData} customValidations={{'compute/numOfVMs/maximum' : this.validateMax, 'compute/numOfVMs/minimum': this.validateMin}} /> <GuestOs onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> + <ComputeFlavors computeFlavorsList={computeFlavorsList} softwareProductId={softwareProductId} componentId={componentId} + version={version} isReadOnlyMode={isReadOnlyMode} isManual={isManual}/> </Form> } </div> ); @@ -60,12 +63,24 @@ class SoftwareProductComponentComputeView extends React.Component { validateMin(value, state) { let maxVal = state.dataMap['compute/numOfVMs/maximum']; - return Validator.validateItem(value,maxVal,'maximum'); + // we are allowed to have an empty maxval, that will allow all minvals. + // if we do not have a minval than there is no point to check it either. + if (value === undefined || maxVal === undefined) { + return { isValid: true, errorText: '' }; + } else { + return Validator.validateItem(value, maxVal,'maximum'); + } } validateMax(value, state) { let minVal = state.dataMap['compute/numOfVMs/minimum']; - return Validator.validateItem(value,minVal,'minimum'); + if (minVal === undefined ) { + // having no minimum is the same as 0, maximum value doesn't need to be checked + // against it. + return { isValid: true, errorText: '' }; + } else { + return Validator.validateItem(value,minVal,'minimum'); + } } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/ComputeFlavors.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/ComputeFlavors.js new file mode 100644 index 0000000000..c72d42c11f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/ComputeFlavors.js @@ -0,0 +1,116 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ComputeFlavorActionHelper from 'sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) => { + return { + onAddComputeClick: (isReadOnlyMode) => ComputeFlavorActionHelper.openComputeEditor(dispatch, {props: {softwareProductId, componentId, isReadOnlyMode, version}}), + onEditCompute: ({computeId, isReadOnlyMode}) => ComputeFlavorActionHelper.loadCompute(dispatch, {softwareProductId, componentId, version, computeId, isReadOnlyMode}), + onDeleteCompute: ({id, name}) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete "${name}"?`), + onConfirmed: () => ComputeFlavorActionHelper.deleteCompute(dispatch, {softwareProductId, componentId, computeId: id, version}) + } + }) + }; +}; + +const computeItemPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string +}); + +class ComputeFlavors extends React.Component { + + static propTypes = { + isReadOnlyMode: React.PropTypes.bool, + isManual: React.PropTypes.bool, + onAddComputeClick: React.PropTypes.func, + computeFlavorsList: React.PropTypes.arrayOf(computeItemPropType) + }; + + state = { + localFilter: '' + }; + + render() { + const {localFilter} = this.state; + const {isReadOnlyMode, isManual, onAddComputeClick, onEditCompute, onDeleteCompute} = this.props; + return ( + <div className='computes-list'> + <ListEditorView + title={i18n('Computes')} + plusButtonTitle={i18n('Add Compute')} + onAdd={isManual ? () => onAddComputeClick(isReadOnlyMode) : null} + isReadOnlyMode={isReadOnlyMode} + onFilter={isManual ? value => this.setState({localFilter: value}) : null} + filterValue={localFilter} + twoColumns> + {this.filterList().map(computeItem => + <ComputeItem key={computeItem.id} + computeItem={computeItem} isReadOnlyMode={isReadOnlyMode} isManual={isManual} + onEditCompute={onEditCompute} onDeleteCompute={onDeleteCompute}/>) + } + </ListEditorView> + </div> + ); + } + + filterList() { + const {computeFlavorsList = []} = this.props; + + const {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return computeFlavorsList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return computeFlavorsList; + } + } +} + +const ComputeItem = ({computeItem, isReadOnlyMode, isManual, onEditCompute, onDeleteCompute}) => { + const {id, name, description} = computeItem; + return ( + <ListEditorItemView + key={'item_' + id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditCompute({computeId: id, isReadOnlyMode})} + onDelete={isManual ? () => onDeleteCompute({id, name}) : null}> + + <div className='list-editor-item-view-field'> + <div className='name'>{name}</div> + </div> + <div className='list-editor-item-view-field'> + <div className='description'>{description}</div> + </div> + </ListEditorItemView> + ); +}; + +export default connect(null, mapActionsToProps, null, {withRef: true})(ComputeFlavors); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx index 7a730d6f94..16bf599834 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx @@ -24,17 +24,6 @@ const GuestOs = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { return( <div> <GridSection title={i18n('Guest OS')} > - <GridItem colSpan={2}> - <Input - data-test-id='guestOS-name' - label={i18n('Guest OS')} - type='text' - onChange={(tools) => onQDataChanged({'compute/guestOS/name' : tools})} - isValid={qgenericFieldInfo['compute/guestOS/name'].isValid} - errorText={qgenericFieldInfo['compute/guestOS/name'].errorText} - value={dataMap['compute/guestOS/name']} /> - </GridItem> - <GridItem colSpan={2}/> <GridItem> <div className='vertical-flex'> <label key='label' className='control-label'>{i18n('OS Bit Size')}</label> @@ -58,6 +47,16 @@ const GuestOs = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { <GridItem colSpan={2}/> <GridItem colSpan={2}> <Input + data-test-id='guestOS-name' + label={i18n('Guest OS')} + type='textarea' + onChange={(tools) => onQDataChanged({'compute/guestOS/name' : tools})} + isValid={qgenericFieldInfo['compute/guestOS/name'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/name'].errorText} + value={dataMap['compute/guestOS/name']} /> + </GridItem> + <GridItem colSpan={2}> + <Input data-test-id='guestOS-tools' type='textarea' label={i18n('Guest OS Tools:')} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx index efeedc653e..ddde4391d9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx @@ -45,44 +45,6 @@ const NumberOfVms = ({qgenericFieldInfo, dataMap, onQDataChanged, qValidateData, errorText={qgenericFieldInfo['compute/numOfVMs/maximum'].errorText} value={dataMap['compute/numOfVMs/maximum']} /> </GridItem> - <GridItem> - <Input - data-test-id='numOfVMs-CpuOverSubscriptionRatio' - label={i18n('CPU Oversubscription Ratio')} - type='select' - groupClassName='bootstrap-input-options' - className='input-options-select' - isValid={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].isValid} - errorText={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].errorText} - value={dataMap['compute/numOfVMs/CpuOverSubscriptionRatio']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'compute/numOfVMs/CpuOverSubscriptionRatio' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].enum.map(cpuOSR => <option value={cpuOSR.enum} key={cpuOSR.enum}>{cpuOSR.title}</option>)} - </Input> - </GridItem> - <GridItem> - <Input - data-test-id='numOfVMs-MemoryRAM' - type='select' - label={i18n('Memory - RAM')} - groupClassName='bootstrap-input-options' - className='input-options-select' - isValid={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].isValid} - errorText={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].errorText} - value={dataMap['compute/numOfVMs/MemoryRAM']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'compute/numOfVMs/MemoryRAM' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].enum.map(mRAM => <option value={mRAM.enum} key={mRAM.enum}>{mRAM.title}</option>)} - </Input> - </GridItem> </GridSection> ); }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx deleted file mode 100644 index 39f84807a2..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -import React from 'react'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Input from 'nfvo-components/input/validation/Input.jsx'; -import GridSection from 'nfvo-components/grid/GridSection.jsx'; -import GridItem from 'nfvo-components/grid/GridItem.jsx'; -const VmSizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { - return( - <GridSection title={i18n('VM Sizing')}> - <GridItem> - <Input - data-test-id='numOfCPUs' - type='number' - label={i18n('Number of CPUs')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/numOfCPUs' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].errorText} - value={dataMap['compute/vmSizing/numOfCPUs']} /> - </GridItem> - <GridItem> - <Input - data-test-id='fileSystemSizeGB' - type='number' - label={i18n('File System Size (GB)')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/fileSystemSizeGB' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].errorText} - value={dataMap['compute/vmSizing/fileSystemSizeGB']} /> - </GridItem> - <GridItem> - <Input - data-test-id='persistentStorageVolumeSize' - type='number' - label={i18n('Persistent Storage/Volume Size (GB)')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/persistentStorageVolumeSize' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].errorText} - value={dataMap['compute/vmSizing/persistentStorageVolumeSize']} /> - </GridItem> - <GridItem> - <Input - data-test-id='IOOperationsPerSec' - type='number' - label={i18n('I/O Operations (per second)')} - onChange={(tools) => onQDataChanged({'compute/vmSizing/IOOperationsPerSec' : tools})} - isValid={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].isValid} - errorText={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].errorText} - value={dataMap['compute/vmSizing/IOOperationsPerSec']} /> - </GridItem> - </GridSection> - ); -}; - -export default VmSizing; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorConstants.js new file mode 100644 index 0000000000..41728eefb0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorConstants.js @@ -0,0 +1,32 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const COMPUTE_FLAVOR_FORM = 'COMPUTE_FLAVOR_FORM'; + +export const actionTypes = keyMirror({ + ADD_COMPUTE: null, + COMPUTE_FLAVORS_LIST_LOADED: null, + COMPUTE_LIST_EDIT: null, + EDIT_COMPUTE_FLAVOR: null, + DELETE_COMPUTE: null, + CONFIRM_DELETE_COMPUTE: null, + computeEditor: { + LOAD_EDITOR_DATA: null, + CLEAR_DATA: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js new file mode 100644 index 0000000000..caec0702fd --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js @@ -0,0 +1,55 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import ComputeFlavorEditorView from './ComputeFlavorEditorView.jsx'; +import {COMPUTE_FLAVOR_FORM} from './ComputeFlavorConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ComputeFlavorActionHelper from 'sdc-app/onboarding/softwareProduct/components/compute/ComputeFlavorActionHelper.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {COMPONENTS_COMPUTE_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {onboardingMethod} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductEditor, softwareProductComponents: {computeFlavor: {computeEditor = {}}}}}) => { + const {data: currentSoftwareProduct = {}} = softwareProductEditor; + const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {data , qdata, qgenericFieldInfo, dataMap, genericFieldInfo, formReady} = computeEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + return { + data, + qdata, + qgenericFieldInfo, + dataMap, + genericFieldInfo, + isReadOnlyMode, + isFormValid, + formReady, + isManual: currentSoftwareProduct.onboardingMethod === onboardingMethod.MANUAL + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) => { + return { + onDataChanged: deltaData => ValidationHelper.dataChanged(dispatch, {deltaData, formName: COMPUTE_FLAVOR_FORM}), + onQDataChanged: deltaData => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_COMPUTE_QUESTIONNAIRE}), + onCancel: () => ComputeFlavorActionHelper.closeComputeEditor(dispatch), + onSubmit: ({data, qdata}) => ComputeFlavorActionHelper.saveComputeDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata, version}), + onValidateForm: () => ValidationHelper.validateForm(dispatch, COMPUTE_FLAVOR_FORM) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ComputeFlavorEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditorView.jsx new file mode 100644 index 0000000000..8f8a504629 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditorView.jsx @@ -0,0 +1,96 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import VmSizing from './VmSizing.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +class ComputeEditorView extends React.Component { + + static propTypes = { + data: React.PropTypes.object, + qdata: React.PropTypes.object, + qschema: React.PropTypes.object, + isReadOnlyMode: React.PropTypes.bool, + isManual: React.PropTypes.bool, + onDataChanged: React.PropTypes.func.isRequired, + onQDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {data = {}, qdata = {}, qgenericFieldInfo, dataMap, genericFieldInfo, isReadOnlyMode, isManual, isFormValid, formReady, + onDataChanged, onQDataChanged, onSubmit, onCancel, onValidateForm} = this.props; + const {id, name, description} = data; + const edittingComputeMode = Boolean(id); + + return ( + <div className='vsp-component-computeFlavor-view'> + {genericFieldInfo && <Form + ref={(form) => { + this.form = form; + }} + hasButtons={true} + onSubmit={ () => onSubmit({data, qdata}) } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={isFormValid} + formReady={formReady} + onValidateForm={() => onValidateForm() } + className='component-questionnaire-validation-form' + submitButtonText={edittingComputeMode ? i18n('Save') : i18n('Create')}> + <GridSection> + <GridItem colSpan={edittingComputeMode ? 2 : 4}> + <Input + disabled={!isManual} + data-test-id='name' + type='text' + label={i18n('Flavor Name')} + value={name} + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo['name'].isValid} + errorText={genericFieldInfo['name'].errorText} + isRequired/> + </GridItem> + <GridItem colSpan={edittingComputeMode ? 2 : 4}> + <Input + data-test-id='description' + type='textarea' + label={i18n('Description')} + value={description} + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo['description'].isValid} + errorText={genericFieldInfo['description'].errorText}/> + </GridItem> + </GridSection> + {edittingComputeMode && <VmSizing qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged}/>} + </Form> + } + </div> + ); + } + + save(){ + return this.form.handleFormSubmit(new Event('dummy')); + } +} + +export default ComputeEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js new file mode 100644 index 0000000000..6c02f36c90 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js @@ -0,0 +1,33 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {actionTypes} from './ComputeFlavorConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.COMPUTE_FLAVORS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.ADD_COMPUTE: + return [...state, action.compute]; + case actionTypes.COMPUTE_LIST_EDIT: + const indexForEdit = state.findIndex(({id}) => id === action.compute.id); + return [...state.slice(0, indexForEdit), action.compute, ...state.slice(indexForEdit + 1)]; + case actionTypes.DELETE_COMPUTE: + return state.filter(({id}) => id !== action.computeId); + default: + return state; + } +};
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js new file mode 100644 index 0000000000..a476f85a19 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorReducer.js @@ -0,0 +1,45 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes, COMPUTE_FLAVOR_FORM} from './ComputeFlavorConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.computeEditor.LOAD_EDITOR_DATA: + return { + ...state, + formName: COMPUTE_FLAVOR_FORM, + data: action.compute, + formReady: null, + genericFieldInfo: { + name: { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true }] + }, + description: { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 300}] + } + } + }; + case actionTypes.computeEditor.CLEAR_DATA: + return {}; + default: + return state; + } +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/VmSizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/VmSizing.jsx new file mode 100644 index 0000000000..8b30468362 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/VmSizing.jsx @@ -0,0 +1,106 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +const VmSizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={i18n('VM Sizing')}> + <GridItem> + <Input + data-test-id='numOfCPUs' + type='number' + label={i18n('Number of CPUs')} + onChange={(tools) => onQDataChanged({'vmSizing/numOfCPUs' : tools})} + isValid={qgenericFieldInfo['vmSizing/numOfCPUs'].isValid} + errorText={qgenericFieldInfo['vmSizing/numOfCPUs'].errorText} + value={dataMap['vmSizing/numOfCPUs']} /> + </GridItem> + <GridItem> + <Input + data-test-id='fileSystemSizeGB' + type='number' + label={i18n('File System Size (GB)')} + onChange={(tools) => onQDataChanged({'vmSizing/fileSystemSizeGB' : tools})} + isValid={qgenericFieldInfo['vmSizing/fileSystemSizeGB'].isValid} + errorText={qgenericFieldInfo['vmSizing/fileSystemSizeGB'].errorText} + value={dataMap['vmSizing/fileSystemSizeGB']} /> + </GridItem> + <GridItem> + <Input + data-test-id='persistentStorageVolumeSize' + type='number' + label={i18n('Persistent Storage/Volume Size (GB)')} + onChange={(tools) => onQDataChanged({'vmSizing/persistentStorageVolumeSize' : tools})} + isValid={qgenericFieldInfo['vmSizing/persistentStorageVolumeSize'].isValid} + errorText={qgenericFieldInfo['vmSizing/persistentStorageVolumeSize'].errorText} + value={dataMap['vmSizing/persistentStorageVolumeSize']} /> + </GridItem> + <GridItem> + <Input + data-test-id='ioOperationsPerSec' + type='number' + label={i18n('I/O Operations (per second)')} + onChange={(tools) => onQDataChanged({'vmSizing/ioOperationsPerSec' : tools})} + isValid={qgenericFieldInfo['vmSizing/ioOperationsPerSec'].isValid} + errorText={qgenericFieldInfo['vmSizing/ioOperationsPerSec'].errorText} + value={dataMap['vmSizing/ioOperationsPerSec']} /> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-cpuOverSubscriptionRatio' + label={i18n('CPU Oversubscription Ratio')} + type='select' + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['vmSizing/cpuOverSubscriptionRatio'].isValid} + errorText={qgenericFieldInfo['vmSizing/cpuOverSubscriptionRatio'].errorText} + value={dataMap['vmSizing/cpuOverSubscriptionRatio']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'vmSizing/cpuOverSubscriptionRatio' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['vmSizing/cpuOverSubscriptionRatio'].enum.map(cpuOSR => <option value={cpuOSR.enum} key={cpuOSR.enum}>{cpuOSR.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-memoryRAM' + type='select' + label={i18n('Memory - RAM')} + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['vmSizing/memoryRAM'].isValid} + errorText={qgenericFieldInfo['vmSizing/memoryRAM'].errorText} + value={dataMap['vmSizing/memoryRAM']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'vmSizing/memoryRAM' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['vmSizing/memoryRAM'].enum.map(mRAM => <option value={mRAM.enum} key={mRAM.enum}>{mRAM.title}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +export default VmSizing; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js new file mode 100644 index 0000000000..e85b6b6504 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js @@ -0,0 +1,50 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentCreationView from './SoftwareProductComponentCreationView.jsx'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {forms} from '../SoftwareProductComponentsConstants.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductComponents: {componentEditor: {data, genericFieldInfo, formReady}}, softwareProductEditor: {data: {version}}} = softwareProduct; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + return { + data, + genericFieldInfo, + formReady, + isFormValid, + version + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + return { + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.CREATE_FORM}), + //onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}), + onSubmit: (componentData, version) => { + return SoftwareProductComponentsActionHelper.createSoftwareProductComponent(dispatch, + {softwareProductId, componentData, version}); + }, + onCancel: () => SoftwareProductComponentsActionHelper.closeComponentCreationModal(dispatch), + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) + }; + +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreationView.jsx new file mode 100644 index 0000000000..55bcc818f5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreationView.jsx @@ -0,0 +1,79 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {forms} from '../SoftwareProductComponentsConstants.js'; + +class ComponentCreationView extends React.Component { + render() { + let {data = {}, onDataChanged, onCancel, genericFieldInfo} = this.props; + let {displayName, description} = data; + return( + <div> + { + genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + submitButtonText={i18n('Create')} + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(forms.CREATE_FORM) } + className='entitlement-pools-form'> + <GridSection> + <GridItem colSpan={4}> + <Input + data-test-id='name' + onChange={displayName => onDataChanged({displayName})} + label={i18n('Name')} + isRequired={true} + isValid={genericFieldInfo.displayName.isValid} + errorText={genericFieldInfo.displayName.errorText} + value={displayName} + type='text'/> + </GridItem> + <GridItem colSpan={4}> + <Input + label={i18n('Description')} + onChange={description => onDataChanged({description})} + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + data-test-id='description' + type='textarea'/> + </GridItem> + </GridSection> + </Form> + } + </div> + ); + } + + submit() { + const {onSubmit, data, version} = this.props; + onSubmit(data, version); + } +} + +export default ComponentCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js index 34374aa7fb..7b4135028b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js @@ -22,6 +22,7 @@ import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/Soft import {forms, COMPONENTS_QUESTIONNAIRE} from '../SoftwareProductComponentsConstants.js'; +import {onboardingMethod} from '../../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct}) => { @@ -34,6 +35,7 @@ export const mapStateToProps = ({softwareProduct}) => { componentData, qdata, isReadOnlyMode, + isManual: currentVSP.onboardingMethod === onboardingMethod.MANUAL, genericFieldInfo, qGenericFieldInfo, dataMap, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx index e4595f97d6..6aa51d1609 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx @@ -21,7 +21,7 @@ import Form from 'nfvo-components/input/validation/Form.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isReadOnlyMode, genericFieldInfo}) => ( +const GeneralSection = ({onDataChanged, displayName, vfcCode, nfcFunction, description, isReadOnlyMode, genericFieldInfo, isManual}) => ( <GridSection title={i18n('General')}> {/* disabled until backend will be ready to implement it <div className='validation-input-wrapper'> @@ -37,9 +37,9 @@ const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isRea data-test-id='name' label={i18n('Name')} value={displayName} - disabled={true} + disabled={!isManual || isReadOnlyMode} type='text'/> - <Input + {!isManual && <Input data-test-id='vfcCode' label={i18n('Naming Code')} value={vfcCode} @@ -47,6 +47,15 @@ const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isRea errorText={genericFieldInfo.vfcCode.errorText} onChange={vfcCode => onDataChanged({vfcCode})} disabled={isReadOnlyMode} + type='text'/> } + <Input + data-test-id='nfcFunction' + label={i18n('Function')} + value={nfcFunction} + isValid={genericFieldInfo.nfcFunction.isValid} + errorText={genericFieldInfo.nfcFunction.errorText} + onChange={nfcFunction => onDataChanged({nfcFunction})} + disabled={isReadOnlyMode} type='text'/> </GridItem> <GridItem colSpan={2}> @@ -63,7 +72,7 @@ const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isRea </GridItem> <GridItem /> </GridSection> - ); +); const HypervisorSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( <GridSection title={i18n('Hypervisor')}> @@ -110,64 +119,26 @@ const HypervisorSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( ); const ImageSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( - <GridSection title={i18n('Image')}> - <GridItem> - <Input - data-test-id='format' - label={i18n('Image format')} - type='select' - className='input-options-select' - groupClassName='bootstrap-input-options' - isValid={qgenericFieldInfo['general/image/format'].isValid} - errorText={qgenericFieldInfo['general/image/format'].errorText} - value={dataMap['general/image/format']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'general/image/format' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['general/image/format'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} - </Input> - </GridItem> - <GridItem> - <Input - data-test-id='providedBy' - label={i18n('Image provided by')} - type='select' - className='input-options-select' - groupClassName='bootstrap-input-options' - isValid={qgenericFieldInfo['general/image/providedBy'].isValid} - errorText={qgenericFieldInfo['general/image/providedBy'].errorText} - value={dataMap['general/image/providedBy']} - onChange={(e) => { - const selectedIndex = e.target.selectedIndex; - const val = e.target.options[selectedIndex].value; - onQDataChanged({'general/image/providedBy' : val});} - }> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {qgenericFieldInfo['general/image/providedBy'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} - </Input> - </GridItem> + <GridSection title={i18n('Disk')}> <GridItem> <Input data-test-id='bootDiskSizePerVM' - onChange={(bootDiskSizePerVM) => onQDataChanged({'general/image/bootDiskSizePerVM' : bootDiskSizePerVM})} + onChange={(bootDiskSizePerVM) => onQDataChanged({'general/disk/bootDiskSizePerVM' : bootDiskSizePerVM})} label={i18n('Size of boot disk per VM (GB)')} type='number' - isValid={qgenericFieldInfo['general/image/bootDiskSizePerVM'].isValid} - errorText={qgenericFieldInfo['general/image/bootDiskSizePerVM'].errorText} - value={dataMap['general/image/bootDiskSizePerVM']}/> + isValid={qgenericFieldInfo['general/disk/bootDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/disk/bootDiskSizePerVM'].errorText} + value={dataMap['general/disk/bootDiskSizePerVM']}/> </GridItem> <GridItem> <Input data-test-id='ephemeralDiskSizePerVM' - onChange={(ephemeralDiskSizePerVM) => onQDataChanged({'general/image/ephemeralDiskSizePerVM' : ephemeralDiskSizePerVM})} + onChange={(ephemeralDiskSizePerVM) => onQDataChanged({'general/disk/ephemeralDiskSizePerVM' : ephemeralDiskSizePerVM})} label={i18n('Size of ephemeral disk per VM (GB)')} type='number' - isValid={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].isValid} - errorText={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].errorText} - value={dataMap['general/image/ephemeralDiskSizePerVM']}/> + isValid={qgenericFieldInfo['general/disk/ephemeralDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/disk/ephemeralDiskSizePerVM'].errorText} + value={dataMap['general/disk/ephemeralDiskSizePerVM']}/> </GridItem> </GridSection> ); @@ -257,7 +228,7 @@ const CloneSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( class SoftwareProductComponentsGeneralView extends React.Component { render() { - let {onQDataChanged, onDataChanged, genericFieldInfo, dataMap, qGenericFieldInfo, componentData: {displayName, vfcCode, description}, isReadOnlyMode} = this.props; + let {isManual, onQDataChanged, onDataChanged, genericFieldInfo, dataMap, qGenericFieldInfo, componentData: {displayName, vfcCode, nfcFunction, description}, isReadOnlyMode} = this.props; return( <div className='vsp-components-general'> <div className='general-data'> @@ -271,7 +242,9 @@ class SoftwareProductComponentsGeneralView extends React.Component { onDataChanged={onDataChanged} displayName={displayName} vfcCode={vfcCode} + nfcFunction={nfcFunction} description={description} + isManual={isManual} isReadOnlyMode={isReadOnlyMode} genericFieldInfo={genericFieldInfo}/> <HypervisorSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js new file mode 100644 index 0000000000..34198281b7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js @@ -0,0 +1,169 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {IMAGE_QUESTIONNAIRE} from './SoftwareProductComponentsImageConstants.js'; +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; + +function baseUrl(softwareProductId, version, componentId) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/images`; +} + +function fetchImagesList({softwareProductId, componentId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); +} + +function fetchImage({softwareProductId, componentId, imageId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${imageId}`); +} + +function destroyImage({softwareProductId, componentId, version, imageId}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/${imageId}`); +} + +function createImage({softwareProductId, componentId, version, data}) { + return RestAPIUtil.post(baseUrl(softwareProductId, version, componentId), { + fileName: data.fileName + }); +} + +function fetchImageQuestionnaire({softwareProductId, componentId, imageId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${imageId}/questionnaire`); +} + +function saveImage({softwareProductId, version, componentId, image: {id, fileName}}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${id}`,{ + fileName + }); + +} + +function saveImageQuestionnaire({softwareProductId, componentId, version, imageId, qdata}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${imageId}/questionnaire`, qdata); +} + +const SoftwareProductComponentImagesActionHelper = { + fetchImagesList(dispatch, {softwareProductId, componentId, version}) { + dispatch({ + type: actionTypes.IMAGES_LIST_UPDATE, + response: [] + }); + + return fetchImagesList({softwareProductId, componentId, version}).then((response) => { + dispatch({ + type: actionTypes.IMAGES_LIST_UPDATE, + response: response.results, + componentId : componentId + }); + }); + }, + + deleteImage(dispatch, {softwareProductId, componentId, version, imageId}) { + return destroyImage({softwareProductId, componentId, version, imageId}).then(() => { + return SoftwareProductComponentImagesActionHelper.fetchImagesList(dispatch, {softwareProductId, componentId, version}); + }); + }, + + loadImageData({softwareProductId, componentId, imageId, version}) { + return fetchImage({softwareProductId, componentId, imageId, version}); + }, + + openEditImageEditor(dispatch, {image, softwareProductId, componentId, version, isReadOnlyMode, modalClassName}) { + return SoftwareProductComponentImagesActionHelper.loadImageData({softwareProductId, componentId, imageId: image.id, version}).then(({data}) => { + SoftwareProductComponentImagesActionHelper.loadImageQuestionnaire(dispatch, { + softwareProductId, + componentId, + imageId: image.id, + version + }).then(() => { + SoftwareProductComponentImagesActionHelper.openImageEditor(dispatch, { + softwareProductId, + componentId, + version, + isReadOnlyMode, + modalClassName, + image, + data + }); + }); + }); + }, + + openImageEditor(dispatch, {image = {}, data = {}, softwareProductId, componentId, version, isReadOnlyMode}) { + + let title = (image && image.id) ? i18n('Edit Image') : i18n('Create New Image'); + let className = (image && image.id) ? 'image-edit-editor-model' : 'image-new-editor-modal'; + + dispatch({ + type: actionTypes.ImageEditor.OPEN, + image: {...data, id: image.id} + }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR, + title: title, + modalComponentProps: {softwareProductId, componentId, version, isReadOnlyMode, dialogClassName:className} + } + }); + }, + + closeImageEditor(dispatch) { + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }); + + dispatch({ + type: actionTypes.ImageEditor.CLOSE + }); + }, + + loadImageQuestionnaire(dispatch, {softwareProductId, componentId, imageId, version}) { + return fetchImageQuestionnaire({softwareProductId, componentId, imageId, version}).then((response) => { + ValidationHelper.qDataLoaded(dispatch, {qName: IMAGE_QUESTIONNAIRE ,response: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + }}); + }); + }, + + saveImageDataAndQuestionnaire(dispatch, {softwareProductId, componentId, version, data, qdata}) { + SoftwareProductComponentImagesActionHelper.closeImageEditor(dispatch); + if (data !== null && data.id) { + // editor in edit mode + return Promise.all([ + saveImageQuestionnaire({softwareProductId, version, componentId, imageId: data.id, qdata}), + saveImage({softwareProductId, version, componentId, image: data}).then(() => { + return SoftwareProductComponentImagesActionHelper.fetchImagesList(dispatch, {softwareProductId, componentId, version}); + }) + ]); + } else { + // editor in create mode + createImage({softwareProductId, componentId, version, data}).then(() => { + return SoftwareProductComponentImagesActionHelper.fetchImagesList(dispatch, {softwareProductId, componentId, version}); + }); + } + } +}; +export default SoftwareProductComponentImagesActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js new file mode 100644 index 0000000000..6b6c9a30e5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js @@ -0,0 +1,27 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + IMAGES_LIST_UPDATE: null, + + ImageEditor: { + CLOSE: null, + OPEN: null + } +}); + +export const IMAGE_QUESTIONNAIRE = 'image'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js new file mode 100644 index 0000000000..49d891c9df --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js @@ -0,0 +1,63 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductComponentsImageActionHelper from './SoftwareProductComponentsImageActionHelper.js'; +import SoftwareProductComponentsImageEditorView from './SoftwareProductComponentsImageEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {IMAGE_QUESTIONNAIRE} from './SoftwareProductComponentsImageConstants.js'; + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + + let {images: {imageEditor = {}}} = softwareProductComponents; + let {data, qdata, genericFieldInfo, qgenericFieldInfo, dataMap, formReady} = imageEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {version, onboardingMethod} = currentSoftwareProduct; + let isManual = onboardingMethod === onboardingMethodTypes.MANUAL; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo) && ValidationHelper.checkFormValid(qgenericFieldInfo); + + return { + version, + currentSoftwareProduct, + isValidityData, + data, + qdata, + dataMap, + isFormValid, + formReady, + genericFieldInfo, + qgenericFieldInfo, + isReadOnlyMode, + isManual: isManual + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) => { + return { + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.IMAGE_EDIT_FORM}), + onSubmit: ({data, qdata}) => SoftwareProductComponentsImageActionHelper.saveImageDataAndQuestionnaire(dispatch, {softwareProductId, componentId, version, data, qdata}), + onCancel: () => SoftwareProductComponentsImageActionHelper.closeImageEditor(dispatch), + onValidateForm: () => ValidationHelper.validateForm(dispatch, forms.IMAGE_EDIT_FORM), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: IMAGE_QUESTIONNAIRE}), + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentsImageEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js new file mode 100644 index 0000000000..0ab785a97f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js @@ -0,0 +1,42 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.ImageEditor.CLOSE: + return {}; + case actionTypes.ImageEditor.OPEN: + return { + ...state, + data: { + ...action.image + }, + genericFieldInfo: { + 'fileName' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'validateName', data: true}] + } + }, + formName: forms.IMAGE_EDIT_FORM + }; + default: + return state; + } +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx new file mode 100644 index 0000000000..300f8edcc3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx @@ -0,0 +1,71 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; + +import FileDetails from './imagesEditorComponents/FileDetails.jsx'; +import ImageDetails from './imagesEditorComponents/ImageDetails.jsx'; + +class SoftwareProductComponentsImageEditorView extends React.Component { + static propTypes = { + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, genericFieldInfo, qgenericFieldInfo, dataMap, onDataChanged, isManual, onQDataChanged} = this.props; + let {id, fileName} = data; + let editingMode = Boolean(id); + return ( + <div> + {genericFieldInfo && <Form + ref={(form) => { this.form = form; }} + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={isFormValid} + formReady={formReady} + submitButtonText={editingMode ? i18n('Save') : i18n('Create')} + onValidateForm={() => onValidateForm() } + className='vsp-components-image-editor'> + <div className='editor-data'> + <FileDetails + editingMode={editingMode} + genericFieldInfo={genericFieldInfo} + qgenericFieldInfo={qgenericFieldInfo} + fileName={fileName} + onDataChanged={onDataChanged} + isManual={isManual} + dataMap={dataMap} + onQDataChanged={onQDataChanged}/> + {editingMode && <ImageDetails dataMap={dataMap}qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} + </div> + </Form>} + </div> + ); + } + submit() { + let {data, qdata, onSubmit, version} = this.props; + onSubmit({data, qdata, version}); + } +} + +export default SoftwareProductComponentsImageEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js new file mode 100644 index 0000000000..86c4e072d4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js @@ -0,0 +1,88 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import SoftwareProductComponentsImageListView from './SoftwareProductComponentsImageListView.jsx'; +import ImageHelper from './SoftwareProductComponentsImageActionHelper.js'; +import SoftwareProductComponentsImagesActionHelper from './SoftwareProductComponentsImageActionHelper.js'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; + +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + let {images: {imagesList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {version, onboardingMethod} = currentSoftwareProduct; + let isManual = onboardingMethod === onboardingMethodTypes.MANUAL; + + return { + version, + componentData, + qdata, + dataMap, + qgenericFieldInfo, + isValidityData, + imagesList, + isReadOnlyMode, + isManual : isManual + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: COMPONENTS_QUESTIONNAIRE}), + onAddImage: (version, isReadOnlyMode) => { + SoftwareProductComponentsImagesActionHelper.openImageEditor(dispatch, + {isReadOnlyMode, softwareProductId, + componentId, version} + );}, + onDeleteImage: ((image, version) => { + let shortenedFileName = (image.fileName.length > 40) ? image.fileName.substr(0,40) + '...' : image.fileName; + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data: { + msg: i18n(`Are you sure you want to delete "${shortenedFileName}"?`), + onConfirmed: () => ImageHelper.deleteImage(dispatch, { + softwareProductId, + componentId, + version, + imageId: image.id + }) + } + }); + }), + onEditImageClick: (image, version, isReadOnlyMode) => { + SoftwareProductComponentsImagesActionHelper.openEditImageEditor(dispatch, { + image, isReadOnlyMode, softwareProductId, componentId, version, modalClassName: 'image-modal-edit'} + ); + }, + onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + {softwareProductId, + vspComponentId: componentId, + qdata}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsImageListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListReducer.js new file mode 100644 index 0000000000..5dd2fb623b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListReducer.js @@ -0,0 +1,26 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + + case actionTypes.IMAGES_LIST_UPDATE: + return [...action.response]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx new file mode 100644 index 0000000000..ccf5b9d6b1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx @@ -0,0 +1,132 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; +import Input from'nfvo-components/input/validation/Input.jsx'; + +class SoftwareProductComponentsImageListView extends React.Component { + state = { + localFilter: '' + }; + + render() { + let {dataMap, onQDataChanged, isReadOnlyMode, qgenericFieldInfo} = this.props; + return( + <div className='vsp-components-image'> + <div className='image-data'> + <div> + { qgenericFieldInfo && <Form + formReady={null} + isValid={true} + onSubmit={() => this.save()} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> + + <h3 className='section-title'>{i18n('Image')}</h3> + <div className='rows-section'> + <div className='row-flex-components'> + <div className='single-col'> + <Input + data-test-id='providedBy' + label={i18n('Image provided by')} + type='select' + isValid={qgenericFieldInfo['general/image/providedBy'].isValid} + errorText={qgenericFieldInfo['general/image/providedBy'].errorText} + value={dataMap['general/image/providedBy']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/image/providedBy' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + { qgenericFieldInfo['general/image/providedBy'].enum.map(proto => + <option value={proto.enum} key={proto.enum}>{proto.title}</option>) } + </Input> + </div> + <div className='empty-two-col' /> + </div> + </div> + + </Form> } + </div> + </div> + {this.renderImagesList()} + </div> + ); + }; + + renderImagesList() { + const {localFilter} = this.state; + let {isReadOnlyMode, onAddImage, isManual, version} = this.props; + + return ( + <ListEditorView + title={i18n('Images')} + filterValue={localFilter} + placeholder={i18n('Filter Images by Name')} + isReadOnlyMode={isReadOnlyMode} + onFilter={value => this.setState({localFilter: value})} + onAdd={isManual ? () => onAddImage(version, isReadOnlyMode) : null} + plusButtonTitle={i18n('Add Image')} + twoColumns> + {this.filterList().map(image => this.renderImagesListItem(image, isReadOnlyMode))} + </ListEditorView> + ); + }; + + + renderImagesListItem(image, isReadOnlyMode) { + let {id, fileName} = image; + let {onEditImageClick, version, isManual, onDeleteImage} = this.props; + return ( + <ListEditorItemView + key={id} + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditImageClick(image, version, isReadOnlyMode)} + onDelete={isManual ? () => onDeleteImage(image, version) : null}> + + <ListEditorItemViewField> + <div className='image-filename-cell'><span className='image-filename'>{fileName}</span></div> + </ListEditorItemViewField> + </ListEditorItemView> + ); + } + + filterList() { + let {imagesList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return imagesList.filter(({fileName = ''}) => { + return escape(fileName).match(filter); + }); + } + else { + return imagesList; + } + } + + save() { + let {onSubmit, qdata} = this.props; + return onSubmit({qdata}); + } +} +export default SoftwareProductComponentsImageListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js new file mode 100644 index 0000000000..20d1f5dd18 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js @@ -0,0 +1,32 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + + case actionTypes.IMAGES_LIST_UPDATE: + if (action.componentId) { + return { + ...state, + [action.componentId] : (action.response && action.response.length > 0) + }; + } + return state; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx new file mode 100644 index 0000000000..ca58b697a2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx @@ -0,0 +1,48 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; + +import Format from './Format.jsx'; +import Version from './Version.jsx'; + +const FileDetails = ({editingMode, fileName, onDataChanged, isManual, dataMap, onQDataChanged, genericFieldInfo, qgenericFieldInfo}) => { + let fileNameCols = (editingMode) ? 3 : 4; + return( + <GridSection> + <GridItem colSpan={fileNameCols}> + <Input + disabled={!isManual} + onChange={fileName => onDataChanged({fileName}, forms.IMAGE_EDIT_FORM)} + label={i18n('Image Name')} + data-test-id='image-filename' + value={fileName} + isValid={genericFieldInfo.fileName.isValid} + errorText={genericFieldInfo.fileName.errorText} + isRequired={true} + type='text' + className='image-filename'/> + </GridItem> + {editingMode && <Version isManual={isManual} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} + {editingMode && <Format isManual={isManual} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged}/>} + </GridSection> + ); +}; +export default FileDetails; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Format.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Format.jsx new file mode 100644 index 0000000000..1f71c6b277 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Format.jsx @@ -0,0 +1,47 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const Format = ({isManual, dataMap, qgenericFieldInfo, onQDataChanged}) => { + return( + <GridItem colSpan={2}> + <Input + disabled={!isManual} + data-test-id='image-format' + type='select' + label={i18n('Format')} + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['format'].isValid} + errorText={qgenericFieldInfo['format'].errorText} + value={dataMap['format']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'format' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['format'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + ); +}; +export default Format; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/ImageDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/ImageDetails.jsx new file mode 100644 index 0000000000..24e54bbbcb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/ImageDetails.jsx @@ -0,0 +1,39 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const ImageDetails = ({dataMap, qgenericFieldInfo, onQDataChanged}) => { + return( + <GridSection title={i18n('Image Details')}> + <GridItem colSpan={2}> + <Input + data-test-id='image-md5' + className='image-md5' + type='text' + label={i18n('md5')} + onChange={(md5) => onQDataChanged({'md5' : md5})} + isValid={qgenericFieldInfo['md5'].isValid} + errorText={qgenericFieldInfo['md5'].errorText} + value={dataMap['md5']}/> + </GridItem> + </GridSection> + ); +}; +export default ImageDetails; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx new file mode 100644 index 0000000000..3cac9a51b8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx @@ -0,0 +1,39 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const Version = ({isManual, dataMap, qgenericFieldInfo, onQDataChanged}) => { + return( + <GridItem colSpan={1}> + <Input + disabled={!isManual} + data-test-id='image-version' + type='text' + className='image-version' + label={i18n('Version')} + onChange={(version) => onQDataChanged({'version' : version})} + isValid={qgenericFieldInfo['version'].isValid} + errorText={qgenericFieldInfo['version'].errorText} + value={dataMap['version']}/> + </GridItem> + ); +}; +export default Version; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx index dc86771400..9ae9e359b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -14,7 +14,7 @@ * permissions and limitations under the License. */ import React from 'react'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Form from 'nfvo-components/input/validation/Form.jsx'; @@ -56,7 +56,7 @@ const TextAreaItem = ({item, toggle, expanded, genericFieldInfo, dataMap, onQDat <div className={expanded ? 'title' : 'title add-padding'} data-test-id={`btn-${item.key}`} onClick={() => toggle(item.key)}> - <SVGIcon name={expanded ? 'chevron-up' : 'chevron-down'}/> + <SVGIcon name={expanded ? 'chevronUp' : 'chevronDown'}/> <span className='title-text'>{i18n(item.description)}</span> {item.added && <div className='new-line'>{i18n(item.added)}</div>} </div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js index 293e252dca..730beba545 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js @@ -25,20 +25,19 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data:currentVSP = {}}, softwareProductComponents: {monitoring}} = softwareProduct; - let {trapFilename, pollFilename} = monitoring; + let filenames = monitoring; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { isReadOnlyMode, - trapFilename, - pollFilename + filenames }; }; const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { onDropMibFileToUpload: (formData, type) => - SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, { + SoftwareProductComponentsMonitoringAction.uploadFile(dispatch, { softwareProductId, version, componentId, @@ -46,7 +45,7 @@ const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) type }), - onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, { + onDeleteFile: type => SoftwareProductComponentsMonitoringAction.deleteFile(dispatch, { softwareProductId, version, componentId, @@ -57,7 +56,7 @@ const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) type: modalActionTypes.GLOBAL_MODAL_ERROR, data: { title: i18n('Upload Failed'), - msg: i18n('Expected "zip" file. Please check the provided file type.') + msg: i18n('Expected "zip" file. Please check the provided file type.') } }), diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js index 64403faa78..3db708bc92 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js @@ -16,23 +16,13 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -import SoftwareProductComponentsMonitoringConstants, {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; +import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; -const UPLOAD = true; - function baseUrl(vspId, version, componentId) { const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/components/${componentId}/monitors`; -} - -function snmpTrapUrl(vspId, version, componentId, isUpload) { - return `${baseUrl(vspId, version, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; -} - -function snmpPollUrl(vspId, version, componentId, isUpload) { - return `${baseUrl(vspId, version, componentId)}/snmp${isUpload ? '/upload' : ''}`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/components/${componentId}/uploads`; } let onInvalidFileSizeUpload = (dispatch) => dispatch({ @@ -43,62 +33,42 @@ let onInvalidFileSizeUpload = (dispatch) => dispatch({ } }); -let uploadSnmpTrapFile = (dispatch, {softwareProductId, version, componentId, formData}) => { - RestAPIUtil.post(snmpTrapUrl(softwareProductId, version, componentId, UPLOAD), formData).then(()=> dispatch({ - type: actionTypes.SNMP_TRAP_UPLOADED, data: {filename: formData.get('upload').name} +let uploadFile = (dispatch, {softwareProductId, version, componentId, formData, type}) => { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version, componentId)}/types/${type}`, formData).then(()=> dispatch({ + type: actionTypes.MONITOR_UPLOADED, data: {filename: formData.get('upload').name, type : type} })); }; -let uploadSnmpPollFile = (dispatch, {softwareProductId, version, componentId, formData}) => { - RestAPIUtil.post(snmpPollUrl(softwareProductId, version, componentId, UPLOAD), formData).then(()=> dispatch({ - type: actionTypes.SNMP_POLL_UPLOADED, data: {filename: formData.get('upload').name} +let deleteFile = (dispatch, {softwareProductId, version, componentId, type}) => { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/types/${type}`).then(()=> dispatch({ + type: actionTypes.MONITOR_DELETED, + data : { type: type} })); }; -let deleteSnmpTrapFile = (dispatch, {softwareProductId, version, componentId}) => { - RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ - type: actionTypes.SNMP_TRAP_DELETED - })); -}; - -let deleteSnmpPollFile = (dispatch, {softwareProductId, version, componentId}) => { - RestAPIUtil.destroy(snmpPollUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ - type: actionTypes.SNMP_POLL_DELETED - })); -}; const SoftwareProductComponentsMonitoringAction = { fetchExistingFiles(dispatch, {softwareProductId, version, componentId}){ - RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/snmp`).then(response => + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`).then(response => dispatch({ - type: actionTypes.SNMP_FILES_DATA_CHANGE, - data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll} + type: actionTypes.MONITOR_FILES_DATA_CHANGE, + data: response }) ); }, - uploadSnmpFile(dispatch, {softwareProductId, version, componentId, formData, type}){ + uploadFile(dispatch, {softwareProductId, version, componentId, formData, type}){ if (formData.get('upload').size) { - if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - uploadSnmpTrapFile(dispatch, {softwareProductId, version, componentId, formData}); - } - else { - uploadSnmpPollFile(dispatch, {softwareProductId, version, componentId, formData}); - } + return uploadFile(dispatch, {softwareProductId, version, componentId, formData, type}); } else { onInvalidFileSizeUpload(dispatch); } }, - deleteSnmpFile(dispatch, {softwareProductId, version, componentId, type}){ - if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - deleteSnmpTrapFile(dispatch, {softwareProductId, version, componentId}); - } - else { - deleteSnmpPollFile(dispatch, {softwareProductId, version, componentId}); - } + deleteFile(dispatch, {softwareProductId, version, componentId, type}){ + return deleteFile(dispatch, {softwareProductId, version, componentId, type}); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js index d908d36aaa..bf2cbd2a3f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js @@ -14,20 +14,31 @@ * permissions and limitations under the License. */ import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + export const actionTypes = keyMirror({ + MONITOR_FILES_DATA_CHANGE: null, + MONITOR_UPLOADED: null, + MONITOR_DELETED: null +}); - SNMP_FILES_DATA_CHANGE: null, +export const fileTypes = { + SNMP_TRAP: 'SNMP_TRAP', + SNMP_POLL: 'SNMP_POLL', + VES_EVENT: 'VES_EVENTS' +}; - SNMP_TRAP_UPLOADED: null, - SNMP_POLL_UPLOADED: null, +export const type2Name = { + SNMP_TRAP: 'snmpTrap', + SNMP_POLL: 'snmpPoll', + VES_EVENTS: 'vesEvent' +}; - SNMP_TRAP_DELETED: null, - SNMP_POLL_DELETED: null -}); -export default keyMirror({ - SNMP_TRAP: null, - SNMP_POLL: null -}); +export const type2Title = { + SNMP_TRAP : i18n('SNMP Trap'), + SNMP_POLL : i18n('SNMP Poll'), + VES_EVENTS: i18n('VES') +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js index 54513b9634..f5cfe6f06d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js @@ -13,35 +13,21 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ -import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; +import {actionTypes, type2Name} from './SoftwareProductComponentsMonitoringConstants.js'; export default (state = {}, action) => { switch (action.type) { - case actionTypes.SNMP_FILES_DATA_CHANGE: + case actionTypes.MONITOR_FILES_DATA_CHANGE: + return action.data; + case actionTypes.MONITOR_UPLOADED: return { ...state, - trapFilename: action.data.trapFilename, - pollFilename: action.data.pollFilename + [type2Name[action.data.type]]: action.data.filename }; - case actionTypes.SNMP_TRAP_UPLOADED: + case actionTypes.MONITOR_DELETED: return { ...state, - trapFilename: action.data.filename - }; - case actionTypes.SNMP_POLL_UPLOADED: - return { - ...state, - pollFilename: action.data.filename - }; - case actionTypes.SNMP_TRAP_DELETED: - return { - ...state, - trapFilename: undefined - }; - case actionTypes.SNMP_POLL_DELETED: - return { - ...state, - pollFilename: undefined + [type2Name[action.data.type]]: undefined }; default: return state; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx index 329cc70353..2ad48ec84b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -19,14 +19,15 @@ import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar.js'; import Button from 'react-bootstrap/lib/Button.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import SoftwareProductComponentsMonitoringConstants from './SoftwareProductComponentsMonitoringConstants.js'; +import {fileTypes, type2Title, type2Name} from './SoftwareProductComponentsMonitoringConstants.js'; + + class SoftwareProductComponentsMonitoringView extends Component { static propTypes = { isReadOnlyMode: PropTypes.bool, - trapFilename: PropTypes.string, - pollFilename: PropTypes.string, + filenames: PropTypes.object, softwareProductId: PropTypes.string, onDropMibFileToUpload: PropTypes.func, @@ -38,26 +39,24 @@ class SoftwareProductComponentsMonitoringView extends Component { }; + + render() { return ( <div className='vsp-component-monitoring'> - {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_TRAP)} - {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_POLL)} + {this.renderDropzoneWithType(fileTypes.VES_EVENT)} + {this.renderDropzoneWithType(fileTypes.SNMP_TRAP)} + {this.renderDropzoneWithType(fileTypes.SNMP_POLL)} </div> ); } renderDropzoneWithType(type) { - let {isReadOnlyMode, trapFilename, pollFilename} = this.props; - let fileName; - if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - fileName = trapFilename; - } - else { - fileName = pollFilename; - } + let {isReadOnlyMode, filenames} = this.props; + let fileByType = type2Name[type]; + let fileName = (filenames) ? filenames[fileByType] : undefined; let refAndName = `fileInput${type.toString()}`; - let typeDisplayName = this.getFileTypeDisplayName(type); + let typeDisplayName = type2Title[type]; return ( <Dropzone className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`} @@ -97,7 +96,7 @@ class SoftwareProductComponentsMonitoringView extends Component { <ButtonToolbar> <ButtonGroup> <Button disabled>{filename}</Button> - <Button className='delete-button' onClick={()=>this.props.onDeleteSnmpFile(type)}>X</Button> + <Button className='delete-button' onClick={()=>this.props.onDeleteFile(type)}>X</Button> </ButtonGroup> </ButtonToolbar> ); @@ -126,11 +125,6 @@ class SoftwareProductComponentsMonitoringView extends Component { this.props.onFileUploadError(); } } - - getFileTypeDisplayName(type) { - return type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP ? 'SNMP Trap' : 'SNMP Poll'; - } - } export default SoftwareProductComponentsMonitoringView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js new file mode 100644 index 0000000000..865367a734 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js @@ -0,0 +1,51 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import NICCreationActionHelper from './NICCreationActionHelper.js'; +import NICCreationView from './NICCreationView.jsx'; +import SoftwareProductComponentsNetworkActionHelper from '../SoftwareProductComponentsNetworkActionHelper.js'; +import {networkTypes, NIC_CREATION_FORM_NAME} from '../SoftwareProductComponentsNetworkConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents} = softwareProduct; + let {network: {nicCreation = {}}} = softwareProductComponents; + let {data, genericFieldInfo, formReady} = nicCreation; + data = {...data, networkType: networkTypes.EXTERNAL}; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + return { + currentSoftwareProduct, + data, + genericFieldInfo, + isFormValid, + formReady + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => ValidationHelper.dataChanged(dispatch, {deltaData, formName: NIC_CREATION_FORM_NAME}), + onCancel: () => NICCreationActionHelper.close(dispatch), + onSubmit: ({nic, softwareProductId, componentId, version}) => { + NICCreationActionHelper.close(dispatch); + SoftwareProductComponentsNetworkActionHelper.createNIC(dispatch, {nic, softwareProductId, componentId, version}); + }, + onValidateForm: () => ValidationHelper.validateForm(dispatch, NIC_CREATION_FORM_NAME) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(NICCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationActionHelper.js new file mode 100644 index 0000000000..ad28c86b81 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationActionHelper.js @@ -0,0 +1,47 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from '../SoftwareProductComponentsNetworkConstants'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; + +export default { + + open(dispatch, {softwareProductId, componentId, modalClassName}) { + dispatch({ + type: actionTypes.NICCreation.OPEN + }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.NIC_CREATION, + title: i18n('Create NEW NIC'), + modalClassName, + modalComponentProps: {softwareProductId, componentId} + } + }); + }, + + close(dispatch){ + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }); + dispatch({ + type: actionTypes.NICCreation.CLEAR_DATA + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationReducer.js new file mode 100644 index 0000000000..c7e2495b3d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationReducer.js @@ -0,0 +1,49 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes, NIC_CREATION_FORM_NAME} from '../SoftwareProductComponentsNetworkConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.NICCreation.OPEN: + return { + ...state, + data: {}, + formName: NIC_CREATION_FORM_NAME, + formReady: null, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data : true}] + }, + 'networkDescription' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 50}] + } + } + }; + case actionTypes.NICCreation.CLEAR_DATA: + return {}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationView.jsx new file mode 100644 index 0000000000..3cb731a421 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreationView.jsx @@ -0,0 +1,123 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const NICPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + networkId: React.PropTypes.string +}); + +class NICCreationView extends React.Component { + + static propTypes = { + data: NICPropType, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {data = {}, onDataChanged, genericFieldInfo, isFormValid, onValidateForm, formReady} = this.props; + let {name, description, networkDescription} = data; + return ( + <div> + {genericFieldInfo && <Form + ref={(form) => this.form = form} + hasButtons={true} + onSubmit={ () => this.submit() } + submitButtonText={data.id ? i18n('Save') : i18n('Create')} + onReset={ () => this.props.onCancel() } + labledButtons={true} + isValid={isFormValid} + onValidateForm={() => onValidateForm()} + formReady={formReady} > + <GridSection> + <GridItem colSpan={4}> + <Input + value={name} + label={i18n('Name')} + data-test-id='nic-name' + onChange={name => onDataChanged({name})} + isRequired={true} + type='text' + isValid={genericFieldInfo['name'].isValid} + errorText={genericFieldInfo['name'].errorText} + className='field-section'/> + <Input + value={description} + label={i18n('Description')} + data-test-id='nic-description' + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo['description'].isValid} + errorText={genericFieldInfo['description'].errorText} + type='textarea' + className='field-section'/> + </GridItem> + </GridSection> + <GridSection title={i18n('Network')}> + <GridItem colSpan={2}> + <div className='form-group'> + <label className='control-label'>{i18n('Network Type')}</label> + <div className='network-type-radio'> + <Input + label={i18n('Internal')} + disabled + checked={false} + data-test-id='nic-internal' + className='network-radio disabled' + type='radio'/> + <Input + label={i18n('External')} + disabled + checked={true} + data-test-id='nic-external' + className='network-radio disabled' + type='radio'/> + </div> + </div> + </GridItem> + <GridItem colSpan={2}> + <Input + value={networkDescription} + label={i18n('Network Description')} + data-test-id='nic-network-description' + onChange={networkDescription => onDataChanged({networkDescription})} + isValid={genericFieldInfo['networkDescription'].isValid} + errorText={genericFieldInfo['networkDescription'].errorText} + type='text' + className='field-section'/> + </GridItem> + </GridSection> + </Form>} + </div> + ); + } + + + submit() { + const {data: nic, softwareProductId, componentId, currentSoftwareProduct} = this.props; + this.props.onSubmit({nic, softwareProductId, componentId, version: currentSoftwareProduct.version}); + } +} + +export default NICCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js index 7cf1f0189e..b47c7e0f99 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js @@ -20,6 +20,7 @@ import VersionControllerUtils from 'nfvo-components/panel/versionController/Vers import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct}) => { @@ -28,6 +29,7 @@ export const mapStateToProps = ({softwareProduct}) => { let {network: {nicEditor = {}}} = softwareProductComponents; let {data, qdata, genericFieldInfo, qgenericFieldInfo, dataMap, formReady} = nicEditor; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {onboardingMethod} = currentSoftwareProduct; let protocols = []; if(qdata && qdata.protocols && qdata.protocols.protocols && qdata.protocols.protocols.length){ protocols = qdata.protocols.protocols; @@ -47,7 +49,8 @@ export const mapStateToProps = ({softwareProduct}) => { genericFieldInfo, qgenericFieldInfo, isReadOnlyMode, - protocols + protocols, + isManual: onboardingMethod === onboardingMethodTypes.MANUAL }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js index b3c9fe5d98..dd37135d77 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js @@ -18,7 +18,7 @@ import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProdu export default (state = {}, action) => { switch (action.type) { - case actionTypes.NICEditor.OPEN: + case actionTypes.NICEditor.FILL_DATA: return { ...state, data: action.nic, @@ -31,12 +31,17 @@ export default (state = {}, action) => { 'name' : { isValid: true, errorText: '', + validations: [{type: 'required', data : true}] + }, + 'networkDescription' : { + isValid: true, + errorText: '', validations: [] } }, formName: forms.NIC_EDIT_FORM }; - case actionTypes.NICEditor.CLOSE: + case actionTypes.NICEditor.CLEAR_DATA: return {}; default: return state; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx index aad06c82f0..8a4c55a411 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx @@ -28,8 +28,9 @@ import NameAndPurpose from './nicEditorComponents/NameAndPurpose.jsx'; class SoftwareProductComponentsNetworkEditorView extends React.Component { render() { - let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, qgenericFieldInfo, dataMap, onDataChanged, protocols, onQDataChanged} = this.props; - let {name, description, networkName} = data; + let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, qgenericFieldInfo, + dataMap, onDataChanged, protocols, onQDataChanged, isManual, genericFieldInfo} = this.props; + let {name, description, networkName, networkType, networkDescription} = data; let netWorkValues = [{ enum: networkName, title: networkName @@ -48,10 +49,10 @@ class SoftwareProductComponentsNetworkEditorView extends React.Component { onValidateForm={() => onValidateForm() } className='vsp-components-network-editor'> <div className='editor-data'> - <NameAndPurpose name={name} description={description} onDataChanged={onDataChanged} isReadOnlyMode={isReadOnlyMode}/> + <NameAndPurpose isManual={isManual} name={name} description={description} onDataChanged={onDataChanged} isReadOnlyMode={isReadOnlyMode} genericFieldInfo={genericFieldInfo} /> <Protocols protocols={protocols} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> <IpConfig dataMap={dataMap} onQDataChanged={onQDataChanged} /> - <Network networkValues={netWorkValues} /> + <Network networkDescription={networkDescription} onDataChanged={onDataChanged} networkValues={netWorkValues} isReadOnlyMode={isReadOnlyMode} networkType={networkType} /> <Sizing qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> <InFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> <OutFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js index bc061469b1..a3cfe65128 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js @@ -15,8 +15,11 @@ */ import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; +import {actionTypes as GlobalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper as modalPagesMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; @@ -26,6 +29,14 @@ function baseUrl(softwareProductId, version, componentId) { return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/nics`; } +function createNIC({nic, vspId, componentId, version}) { + return RestAPIUtil.post(baseUrl(vspId, version, componentId), { + name: nic.name, + description: nic.description, + networkDescription: nic.networkDescription, + networkType: nic.networkType + }); +} function fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}) { return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${nicId}/questionnaire`); @@ -39,11 +50,16 @@ function fetchNICsList({softwareProductId, version, componentId}) { return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); } -function saveNIC({softwareProductId, version, componentId, nic: {id, name, description, networkId}}) { +function deleteNIC({softwareProductId, componentId, nicId, version}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/${nicId}`); +} +function saveNIC({softwareProductId, version, componentId, nic: {id, name, description, networkId, networkType, networkDescription}}) { return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${id}`,{ name, description, - networkId + networkId, + networkDescription, + networkType }); } @@ -62,23 +78,45 @@ const SoftwareProductComponentNetworkActionHelper = { }); }, - openNICEditor(dispatch, {nic = {}, data = {}}) { + openNICEditor(dispatch, {nic = {}, data = {}, softwareProductId, componentId, isReadOnlyMode, modalClassName}) { dispatch({ - type: actionTypes.NICEditor.OPEN, + type: actionTypes.NICEditor.FILL_DATA, nic: {...data, id: nic.id} }); + dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_SHOW, + data: { + modalClassName, + modalComponentProps: {softwareProductId, componentId, isReadOnlyMode}, + modalComponentName: modalPagesMapper.NIC_EDITOR, + title: i18n('Edit NIC') + } + }); }, closeNICEditor(dispatch) { dispatch({ - type: actionTypes.NICEditor.CLOSE + type: GlobalModalActions.GLOBAL_MODAL_CLOSE + }); + dispatch({ + type: actionTypes.NICEditor.CLEAR_DATA }); }, + createNIC(dispatch, {nic, softwareProductId, componentId, version}){ + return createNIC({nic, vspId: softwareProductId, componentId, version}).then(() => { + return SoftwareProductComponentNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); + }); + }, loadNICData({softwareProductId, version, componentId, nicId}) { return fetchNIC({softwareProductId, version, componentId, nicId}); }, + deleteNIC(dispatch, {softwareProductId, componentId, nicId, version}) { + return deleteNIC({softwareProductId, componentId, nicId, version}).then(() => { + return SoftwareProductComponentNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); + }); + }, loadNICQuestionnaire(dispatch, {softwareProductId, version, componentId, nicId}) { return fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}).then((response) => { ValidationHelper.qDataLoaded(dispatch, {qName: NIC_QUESTIONNAIRE ,response: { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js index 39c55d876c..8ef8fe8c18 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js @@ -20,9 +20,19 @@ export const actionTypes = keyMirror({ NIC_LIST_UPDATE: null, NICEditor: { + FILL_DATA: null, + CLEAR_DATA: null, + }, + NICCreation: { OPEN: null, - CLOSE: null - } + CLEAR_DATA: null, + DATA_CHANGED: null + }, }); +export const networkTypes = { + EXTERNAL: 'External', + INTERNAL: 'Internal' +}; export const NIC_QUESTIONNAIRE = 'nic'; +export const NIC_CREATION_FORM_NAME = 'nicCreation'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js index c2bd8ce479..0fa877e90f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js @@ -14,6 +14,7 @@ * permissions and limitations under the License. */ import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; @@ -21,16 +22,17 @@ import SoftwareProductComponentsNetworkListView from './SoftwareProductComponent import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {actionTypes as GlobalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; +import NICCreationActionHelper from './NICCreation/NICCreationActionHelper.js'; +import {onboardingMethod as onboardingMethodTypes} from '../../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; - let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; - let {data} = nicEditor; + let {network: {nicList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); - let {version} = currentSoftwareProduct; - let isModalInEditMode = true; + let {version, onboardingMethod} = currentSoftwareProduct; return { version, @@ -40,9 +42,8 @@ export const mapStateToProps = ({softwareProduct}) => { qgenericFieldInfo, isValidityData, nicList, - isDisplayModal: Boolean(data), - isModalInEditMode, - isReadOnlyMode + isReadOnlyMode, + isManual: onboardingMethod === onboardingMethodTypes.MANUAL }; }; @@ -51,7 +52,16 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { return { onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), - onEditNicClick: (nic, version) => { + onAddNic: () => NICCreationActionHelper.open(dispatch, {softwareProductId, componentId, modalClassName: 'network-nic-modal-create'}), + onDeleteNic: (nic, version) => dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete "${nic.name}"?`), + onConfirmed: () => SoftwareProductComponentsNetworkActionHelper.deleteNIC(dispatch, {softwareProductId, + componentId, nicId: nic.id, version}) + } + }), + onEditNicClick: (nic, version, isReadOnlyMode) => { Promise.all([ SoftwareProductComponentsNetworkActionHelper.loadNICData({ softwareProductId, @@ -66,7 +76,8 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { nicId: nic.id }) ]).then( - ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data}) + ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data, + isReadOnlyMode, softwareProductId, componentId, modalClassName: 'network-nic-modal-edit'}) ); }, onSubmit: ({qdata, version}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx index f715016ba3..5a159b4a87 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -21,9 +21,7 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; import Input from'nfvo-components/input/validation/Input.jsx'; -import Modal from 'nfvo-components/modal/Modal.jsx'; -import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js'; class SoftwareProductComponentsNetworkView extends React.Component { @@ -32,7 +30,7 @@ class SoftwareProductComponentsNetworkView extends React.Component { }; render() { - let {dataMap, qgenericFieldInfo, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; + let {dataMap, qgenericFieldInfo, onQDataChanged, isReadOnlyMode} = this.props; return( <div className='vsp-components-network'> @@ -85,26 +83,14 @@ class SoftwareProductComponentsNetworkView extends React.Component { </div> {this.renderNicList()} </div> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='network-nic-modal'> - <Modal.Header> - <Modal.Title>{isModalInEditMode ? i18n('Edit NIC') : i18n('Create New NIC')}</Modal.Title> - </Modal.Header> - <Modal.Body> - { - <SoftwareProductComponentsNICEditor - softwareProductId={softwareProductId} - componentId={componentId} - isReadOnlyMode={isReadOnlyMode}/> - } - </Modal.Body> - </Modal> + </div> ); } renderNicList() { const {localFilter} = this.state; - let {isReadOnlyMode} = this.props; + let {isReadOnlyMode, onAddNic, isManual} = this.props; return ( <ListEditorView title={i18n('Interfaces')} @@ -112,6 +98,8 @@ class SoftwareProductComponentsNetworkView extends React.Component { placeholder={i18n('Filter NICs by Name')} isReadOnlyMode={isReadOnlyMode} onFilter={value => this.setState({localFilter: value})} + onAdd={isManual ? onAddNic : null} + plusButtonTitle={i18n('Add NIC')} twoColumns> {this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} </ListEditorView> @@ -120,25 +108,26 @@ class SoftwareProductComponentsNetworkView extends React.Component { renderNicListItem(nic, isReadOnlyMode) { let {id, name, description, networkName = ''} = nic; - let {onEditNicClick, version} = this.props; + let {onEditNicClick, version, isManual, onDeleteNic} = this.props; return ( <ListEditorItemView key={id} isReadOnlyMode={isReadOnlyMode} - onSelect={() => onEditNicClick(nic, version)}> + onSelect={() => onEditNicClick(nic, version, isReadOnlyMode)} + onDelete={isManual ? () => onDeleteNic(nic, version) : null}> <ListEditorItemViewField> <div className='name'>{name}</div> </ListEditorItemViewField> <ListEditorItemViewField> - <div className='details'> - <div className='title'>{i18n('Purpose of NIC')}</div> - <div className='description'>{description}</div> + <div className={isManual ? 'details-col' : 'details'}> + <div className={isManual ? 'manual-title' : 'title'}>{i18n('Purpose of NIC')}</div> + <div className={isManual ? 'description' : ''}>{description ? description : i18n('N/A')}</div> </div> - <div className='details'> + {!isManual && <div className='details'> <div className='title'>{i18n('Network')}</div> <div className='artifact-name'>{networkName}</div> - </div> + </div>} </ListEditorItemViewField> </ListEditorItemView> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx index 3dc153d27f..bc692e753c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx @@ -19,7 +19,7 @@ import Input from 'nfvo-components/input/validation/Input.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -const NameAndPurpose = ({onDataChanged, isReadOnlyMode, name, description}) => { +const NameAndPurpose = ({onDataChanged, genericFieldInfo, isReadOnlyMode, name, description, isManual}) => { return ( <GridSection> @@ -28,7 +28,11 @@ const NameAndPurpose = ({onDataChanged, isReadOnlyMode, name, description}) => { label={i18n('Name')} value={name} data-test-id='nic-name' - disabled={true} + disabled={!isManual} + isRequired={true} + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo['name'].isValid} + errorText={genericFieldInfo['name'].errorText} type='text' /> </GridItem> <GridItem colSpan={2}> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx index 43afdbed3a..8d9b79e67f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx @@ -18,15 +18,17 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import Input from 'nfvo-components/input/validation/Input.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import { networkTypes } from '../SoftwareProductComponentsNetworkConstants.js'; -const Network = ({networkValues}) => { +const Network = ({networkValues, networkType, networkDescription, onDataChanged, isReadOnlyMode}) => { + const isExternal = networkType === networkTypes.EXTERNAL; return ( <GridSection title={i18n('Network')}> <GridItem> <Input label={i18n('Internal')} disabled - checked={true} + checked={!isExternal} data-test-id='nic-internal' className='network-radio disabled' type='radio'/> @@ -35,12 +37,21 @@ const Network = ({networkValues}) => { <Input label={i18n('External')} disabled - checked={false} + checked={isExternal} data-test-id='nic-external' className='network-radio disabled' type='radio'/> </GridItem> <GridItem colSpan={2}> + {isExternal ? + <Input + label={i18n('Network Description')} + value={networkDescription} + data-test-id='nic-network-description' + onChange={networkDescription => onDataChanged({networkDescription})} + disabled={isReadOnlyMode} + type='text'/> + : <Input label={i18n('Network')} data-test-id='nic-network' @@ -48,8 +59,8 @@ const Network = ({networkValues}) => { className='input-options-select' groupClassName='bootstrap-input-options' disabled={true} > - {networkValues.map(val => <option key={val.enum} value={val.enum}>{val.title}</option>)} - </Input> + {networkValues.map(val => <option key={val.enum} value={val.enum}>{val.title}</option>)} + </Input>} </GridItem> </GridSection> ); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js index a8cb709194..826201162c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js @@ -47,7 +47,7 @@ const mapActionsToProps = (dispatch, {componentId, softwareProductId}) => { onDeleteProcessClick: (process, version) => dispatch({ type: modalActionTypes.GLOBAL_MODAL_WARNING, data:{ - msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + msg: i18n(`Are you sure you want to delete "${process.name}"?`), onConfirmed: ()=> SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, {process, softwareProductId, version, componentId}) } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx index 650d6d5ebc..93d5ce886a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx @@ -44,7 +44,7 @@ class SoftwareProductProcessesView extends React.Component { return ( <div className='vsp-processes-page'> <div className='software-product-view'> - <div className='software-product-landing-view-right-side flex-column'> + <div className='software-product-landing-view-right-side vsp-components-processes-page flex-column'> {this.renderEditor()} {this.renderProcessList()} </div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js index 3b434e3ba4..a22b517fa0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -17,7 +17,7 @@ import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; -import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {actionTypes as modalActionTypes, modalSizes} from 'nfvo-components/modal/GlobalModalConstants.js'; import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import {actionTypes} from './SoftwareProductCreationConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -48,9 +48,10 @@ const SoftwareProductCreationActionHelper = { type: modalActionTypes.GLOBAL_MODAL_SHOW, data: { modalComponentName: modalContentMapper.SOFTWARE_PRODUCT_CREATION, - title: i18n('New Software Product'), + title: i18n('New Software Product'), modalComponentProps: { - vendorId + vendorId, + size: modalSizes.LARGE } } }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js index f7a738518e..a7db2b2357 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -15,6 +15,7 @@ */ import {actionTypes, SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; + export default (state = {}, action) => { switch (action.type) { case actionTypes.OPEN: @@ -50,6 +51,11 @@ export default (state = {}, action) => { isValid: true, errorText: '', validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}, {type: 'validateName', data: true}] + }, + 'onboardingMethod' : { + isValid: true, + errorText: '', + validations: [{type: 'requiredChooseOption', data: true}] } }, showModal: true diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx index 11b696855b..11f3543e39 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx @@ -18,10 +18,14 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import Validator from 'nfvo-utils/Validator.js'; import Input from 'nfvo-components/input/validation/Input.jsx'; import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + import {SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; +import {onboardingMethod as onboardingMethodConst} from '../SoftwareProductConstants.js'; const SoftwareProductPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -46,7 +50,7 @@ class SoftwareProductCreationView extends React.Component { render() { let {softwareProductCategories, data = {}, onDataChanged, onCancel, genericFieldInfo, disableVendor} = this.props; - let {name, description, vendorId, subCategory} = data; + let {name, description, vendorId, subCategory, onboardingMethod} = data; const vendorList = this.getVendorList(); return ( @@ -58,10 +62,11 @@ class SoftwareProductCreationView extends React.Component { onReset={() => onCancel() } labledButtons={true} isValid={this.props.isFormValid} + submitButtonText={i18n('Create')} formReady={this.props.formReady} onValidateForm={() => this.validate() }> - <div className='software-product-form-row'> - <div className='software-product-inline-section'> + <GridSection> + <GridItem colSpan='2'> <Input value={name} label={i18n('Name')} @@ -76,6 +81,7 @@ class SoftwareProductCreationView extends React.Component { label={i18n('Vendor')} type='select' value={vendorId} + overlayPos='bottom' isRequired={true} disabled={disableVendor} onChange={e => this.onSelectVendor(e)} @@ -108,8 +114,8 @@ class SoftwareProductCreationView extends React.Component { </optgroup>) } </Input> - </div> - <div className='software-product-inline-section'> + </GridItem> + <GridItem colSpan='2' stretch> <Input value={description} label={i18n('Description')} @@ -120,9 +126,10 @@ class SoftwareProductCreationView extends React.Component { errorText={genericFieldInfo.description.errorText} type='textarea' className='field-section' - data-test-id='new-vsp-description' /> - </div> - </div> + data-test-id='new-vsp-description'/> + </GridItem> + </GridSection> + <OnboardingProcedure genericFieldInfo={genericFieldInfo} onboardingMethod={onboardingMethod} onDataChanged={onDataChanged} /> </Form>} </div> ); @@ -174,4 +181,33 @@ class SoftwareProductCreationView extends React.Component { } } +const OnboardingProcedure = ({onboardingMethod, onDataChanged, genericFieldInfo}) => { + return( + <GridSection title={i18n('Onboarding procedure')}> + <GridItem colSpan={4}> + <Input + label={i18n('HEAT file')} + overlayPos='top' + isValid={genericFieldInfo.onboardingMethod.isValid} + checked={onboardingMethod === onboardingMethodConst.HEAT} + errorText={genericFieldInfo.onboardingMethod.errorText} + onChange={() => onDataChanged({onboardingMethod:'HEAT'},SP_CREATION_FORM_NAME)} + type='radio' + data-test-id='new-vsp-creation-procedure-heat' /> + </GridItem> + <GridItem colSpan={4}> + <Input + label={i18n('Manual')} + overlayPos='bottom' + checked={onboardingMethod === onboardingMethodConst.MANUAL} + isValid={genericFieldInfo.onboardingMethod.isValid} + errorText={genericFieldInfo.onboardingMethod.errorText} + onChange={() => onDataChanged({onboardingMethod:'Manual'},SP_CREATION_FORM_NAME)} + type='radio' + data-test-id='new-vsp-creation-procedure-manual' /> + </GridItem> + </GridSection> + ); +}; + export default SoftwareProductCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx index da975a7be2..2e0cd340de 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx @@ -60,6 +60,7 @@ export default class SoftwareProductDependenciesView extends React.Component { <div className='software-product-dependencies-title'>{i18n('Dependencies')}</div> <SelectActionTable columns={['Source', 'Relation Type', 'Target']} + numOfIcons={2} isReadOnlyMode={isReadOnlyMode} onAdd={canAdd ? onAddDependency : undefined} onAddItem={i18n('Add Rule')}> @@ -68,7 +69,8 @@ export default class SoftwareProductDependenciesView extends React.Component { key={dependency.id} onDelete={() => onDataChanged(softwareProductDependencies.filter(currentDependency => currentDependency.id !== dependency.id))} overlayMsg={i18n('There is a loop between selections')} - hasError={dependency.hasCycle}> + hasError={dependency.hasCycle} + hasErrorIndication> <SelectActionTableCell options={this.filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} selected={dependency.sourceId} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeployment.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeployment.js new file mode 100644 index 0000000000..98f773b465 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeployment.js @@ -0,0 +1,52 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import SoftwareProductDeploymentView from './SoftwareProductDeploymentView.jsx'; +import SoftwareProductDeploymentActionHelper from './SoftwareProductDeploymentActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export function mapStateToProps({softwareProduct}) { + let {softwareProductEditor: {data: currentSoftwareProduct = {}},softwareProductComponents: {componentsList}, softwareProductDeployment: {deploymentFlavors}} = softwareProduct; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + return { + isReadOnlyMode, + deploymentFlavors, + componentsList + }; +} + +function mapActionToProps(dispatch, {softwareProductId, version}) { + let modalClassName = 'deployment-flavor-editor'; + return { + onAddDeployment: componentsList => SoftwareProductDeploymentActionHelper.openDeploymentFlavorEditor(dispatch, {softwareProductId, modalClassName, componentsList, version}), + onDeleteDeployment: ({id, model}) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete "${model}"?`), + onConfirmed: () => SoftwareProductDeploymentActionHelper.deleteDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId: id, version}) + } + }), + onEditDeployment: (deploymentFlavor, componentsList) => + SoftwareProductDeploymentActionHelper.fetchDeploymentFlavor({softwareProductId, deploymentFlavorId: deploymentFlavor.id, version}).then(response => + SoftwareProductDeploymentActionHelper + .openDeploymentFlavorEditor(dispatch, {softwareProductId, componentsList, modalClassName, deploymentFlavor: {...response.data, id: response.id}, isEdit: true, version}), + ) + }; +} + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductDeploymentView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js new file mode 100644 index 0000000000..bd802b38f4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentActionHelper.js @@ -0,0 +1,101 @@ +import {actionTypes} from './SoftwareProductDeploymentConstants.js'; +import {actionTypes as GlobalModalActions} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import pickBy from 'lodash/pickBy'; + +function baseUrl(vspId, version) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/deployment-flavors`; +} + +function fetchDeploymentFlavorsList({softwareProductId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); +} + +function fetchDeploymentFlavor({softwareProductId, deploymentFlavorId, version}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}/${deploymentFlavorId}`); +} + +function deleteDeploymentFlavor({softwareProductId, deploymentFlavorId, version}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version)}/${deploymentFlavorId}`); +} + +function createDeploymentFlavor({softwareProductId, data, version}) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, data); +} + +function editDeploymentFlavor({softwareProductId, deploymentFlavorId, data, version}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${deploymentFlavorId}`, data); +} + +const SoftwareProductDeploymentActionHelper = { + fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}) { + return fetchDeploymentFlavorsList({softwareProductId, version}).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_DEPLOYMENT_FLAVORS, + deploymentFlavors: response.results + }); + }); + }, + + fetchDeploymentFlavor({softwareProductId, deploymentFlavorId, version}) { + return fetchDeploymentFlavor({softwareProductId, deploymentFlavorId, version}); + }, + + deleteDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId, version}) { + return deleteDeploymentFlavor({softwareProductId, deploymentFlavorId, version}).then(() => { + return SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + }); + }, + + createDeploymentFlavor(dispatch, {softwareProductId, data, version}) { + return createDeploymentFlavor({softwareProductId, data, version}).then(() => { + return SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + }); + }, + + editDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId, data, version}) { + let dataWithoutId = pickBy(data, (val, key) => key !== 'id'); + return editDeploymentFlavor({softwareProductId, deploymentFlavorId, data: dataWithoutId, version}).then(() => { + return SoftwareProductDeploymentActionHelper.fetchDeploymentFlavorsList(dispatch, {softwareProductId, version}); + }); + }, + + closeDeploymentFlavorEditor(dispatch) { + dispatch({ + type: actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_CLEAR_DATA + }); + dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_CLOSE + }); + }, + + openDeploymentFlavorEditor(dispatch, {softwareProductId, modalClassName, deploymentFlavor = {}, componentsList, isEdit = false, version}) { + let alteredDeploymentFlavor = {...deploymentFlavor}; + if (componentsList.length) { + alteredDeploymentFlavor = {...alteredDeploymentFlavor, componentComputeAssociations: deploymentFlavor.componentComputeAssociations ? + [{...deploymentFlavor.componentComputeAssociations[0], componentId: componentsList[0].id}] + : + [{componentId: componentsList[0].id, computeFlavorId: null}] + }; + } + dispatch({ + type: actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_FILL_DATA, + deploymentFlavor: alteredDeploymentFlavor + }); + dispatch({ + type: GlobalModalActions.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.DEPLOYMENT_FLAVOR_EDITOR, + modalComponentProps: {softwareProductId, version}, + modalClassName, + title: isEdit ? 'Edit Deployment Flavor' : 'Create a New Deployment Flavor' + } + }); + }, +}; + +export default SoftwareProductDeploymentActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentConstants.js new file mode 100644 index 0000000000..51469b461c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentConstants.js @@ -0,0 +1,28 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + FETCH_SOFTWARE_PRODUCT_DEPLOYMENT_FLAVORS: null, + + deploymentFlavorEditor: { + DATA_CHANGED: null, + SOFTWARE_PRODUCT_DEPLOYMENT_FILL_DATA: null, + SOFTWARE_PRODUCT_DEPLOYMENT_CLEAR_DATA: null + } +}); + +export const DEPLOYMENT_FLAVORS_FORM_NAME = 'DEPLOYMENT_FLAVORS_FORM_NAME'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentListReducer.js new file mode 100644 index 0000000000..8eb91e8fcb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentListReducer.js @@ -0,0 +1,25 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductDeploymentConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_DEPLOYMENT_FLAVORS: + return [...action.deploymentFlavors]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentView.jsx new file mode 100644 index 0000000000..81477ecff7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/SoftwareProductDeploymentView.jsx @@ -0,0 +1,94 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; + +export default class SoftwareProductDeployment extends React.Component { + state = { + localFilter: '' + }; + + static propTypes = { + onAddDeployment: React.PropTypes.func.isRequired, + onDeleteDeployment: React.PropTypes.func.isRequired, + onEditDeployment: React.PropTypes.func.isRequired, + isReadOnlyMode: React.PropTypes.bool.isRequired + }; + + render() { + return ( + <div> + {this.renderList()} + </div> + ); + } + + renderList() { + let {onAddDeployment, isReadOnlyMode, componentsList} = this.props; + return ( + <ListEditorView + plusButtonTitle={i18n('Add Deployment Flavor')} + filterValue={this.state.localFilter} + placeholder={i18n('Filter Deployment')} + onAdd={() => onAddDeployment(componentsList)} + isReadOnlyMode={isReadOnlyMode} + title={i18n('Deployment Flavors')} + onFilter={value => this.setState({localFilter: value})} + twoColumns> + {this.filterList().map(deploymentFlavor => this.renderListItem(deploymentFlavor, isReadOnlyMode))} + </ListEditorView> + ); + } + + renderListItem(deploymentFlavor, isReadOnlyMode) { + let {id, model, description} = deploymentFlavor; + let {onEditDeployment, onDeleteDeployment, componentsList} = this.props; + return ( + <ListEditorItemView + key={id} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode} + onSelect={() => onEditDeployment(deploymentFlavor, componentsList)} + onDelete={() => onDeleteDeployment(deploymentFlavor)}> + <ListEditorItemViewField> + <div className='model'>{model}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div className='description'>{description}</div> + </ListEditorItemViewField> + </ListEditorItemView> + ); + } + + filterList() { + let {deploymentFlavors} = this.props; + let {localFilter} = this.state; + + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return deploymentFlavors.filter(({model = '', description = ''}) => { + return escape(model).match(filter) || escape(description).match(filter); + }); + } + else { + return deploymentFlavors; + } + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js new file mode 100644 index 0000000000..6b924a2816 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js @@ -0,0 +1,88 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import SoftwareProductDeploymentEditorView from './SoftwareProductDeploymentEditorView.jsx'; +import SoftwareProdcutDeploymentActionHelper from '../SoftwareProductDeploymentActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; + +import {DEPLOYMENT_FLAVORS_FORM_NAME} from '../SoftwareProductDeploymentConstants.js'; + +export function mapStateToProps({licenseModel, softwareProduct}) { + let { + softwareProductEditor: { + data: currentSoftwareProduct = {} + }, + softwareProductComponents: { + componentsList, + computeFlavor: { + computesList + } + }, + softwareProductDeployment: { + deploymentFlavors, + deploymentFlavorEditor: { + data = {}, + genericFieldInfo, + formReady + } + } + } = softwareProduct; + + let { + featureGroup: { + featureGroupsList + } + } = licenseModel; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + let selectedFeatureGroupsIds = currentSoftwareProduct.licensingData ? currentSoftwareProduct.licensingData.featureGroups || [] : []; + let selectedFeatureGroupsList = featureGroupsList + .filter(featureGroup => selectedFeatureGroupsIds.includes(featureGroup.id)) + .map(featureGroup => ({value: featureGroup.id, label: featureGroup.name})); + + let DFNames = {}; + + deploymentFlavors.map(deployment => { + DFNames[deployment.model] = deployment.id; + }); + + return { + data, + selectedFeatureGroupsList, + genericFieldInfo, + DFNames, + isFormValid, + formReady, + isReadOnlyMode, + componentsList, + computesList, + isEdit: Boolean(data.id) + }; +} + +function mapActionsToProps(dispatch, {softwareProductId, version}) { + return { + onDataChanged: (deltaData, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: DEPLOYMENT_FLAVORS_FORM_NAME, customValidations}), + onClose: () => SoftwareProdcutDeploymentActionHelper.closeDeploymentFlavorEditor(dispatch), + onCreate: data => SoftwareProdcutDeploymentActionHelper.createDeploymentFlavor(dispatch, {softwareProductId, data, version}), + onEdit: data => SoftwareProdcutDeploymentActionHelper.editDeploymentFlavor(dispatch, {softwareProductId, deploymentFlavorId: data.id, data, version}), + onValidateForm: () => ValidationHelper.validateForm(dispatch, DEPLOYMENT_FLAVORS_FORM_NAME) + }; +} + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductDeploymentEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorReducer.js new file mode 100644 index 0000000000..70836e8ff9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorReducer.js @@ -0,0 +1,44 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes, DEPLOYMENT_FLAVORS_FORM_NAME} from '../SoftwareProductDeploymentConstants.js';; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_FILL_DATA: + return { + ...state, + data: action.deploymentFlavor, + formReady: null, + formName: DEPLOYMENT_FLAVORS_FORM_NAME, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 500}] + }, + 'model' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + } + }; + case actionTypes.deploymentFlavorEditor.SOFTWARE_PRODUCT_DEPLOYMENT_CLEAR_DATA: + return {}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorView.jsx new file mode 100644 index 0000000000..2d621cd2f5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditorView.jsx @@ -0,0 +1,137 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import SelectInput from 'nfvo-components/input/SelectInput.jsx'; +import SelectActionTable from 'nfvo-components/table/SelectActionTable.jsx'; +import SelectActionTableRow from 'nfvo-components/table/SelectActionTableRow.jsx'; +import SelectActionTableCell from 'nfvo-components/table/SelectActionTableCell.jsx'; +import Validator from 'nfvo-utils/Validator.js'; + +export default class SoftwareProductDeploymentEditorView extends React.Component { + render() { + let {data, isEdit, onClose, onDataChanged, isReadOnlyMode, selectedFeatureGroupsList, componentsList, computesList, genericFieldInfo} = this.props; + let {model, description, featureGroupId, componentComputeAssociations = []} = data; + let featureGroupsExist = selectedFeatureGroupsList.length > 0; + return ( + <div> + {genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + onSubmit={ () => this.submit() } + submitButtonText={isEdit ? i18n('Save') : i18n('Create')} + onReset={ () => onClose() } + onValidateForm={() => this.validate() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + className='vsp-deployment-editor'> + <GridSection> + <GridItem colSpan={1}> + <Input + onChange={model => onDataChanged({model}, {model: model => this.validateName(model)})} + label={i18n('Model')} + value={model} + data-test-id='deployment-model' + isValid={genericFieldInfo.model.isValid} + errorText={genericFieldInfo.model.errorText} + isRequired={true} + type='text'/> + </GridItem> + <GridItem colSpan={3}> + <Input + onChange={description => onDataChanged({description})} + label={i18n('Description')} + value={description} + data-test-id='deployment-description' + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + type='text'/> + </GridItem> + </GridSection> + <GridSection className={`deployment-feature-groups-section${!featureGroupsExist ? ' no-feature-groups' : ''}`} title={i18n('License Details')}> + <GridItem colSpan={1}> + <SelectInput + data-test-id='deployment-feature-groups' + label={i18n('Feature Group')} + value={featureGroupId} + onChange={featureGroup => onDataChanged({featureGroupId: featureGroup ? featureGroup.value : null})} + type='select' + clearable={true} + disabled={isReadOnlyMode || !featureGroupsExist} + className='field-section' + options={selectedFeatureGroupsList}/> + </GridItem> + </GridSection> + {!featureGroupsExist && <GridSection className='deployment-feature-group-warning-section'> + <GridItem colSpan={3}> + <span>{i18n('Please assign Feature Groups in VSP General')}</span> + </GridItem> + </GridSection>} + <GridSection title={i18n('Assign VFCs and Compute Flavors')} className='vfc-table'> + <GridItem colSpan={4}> + <SelectActionTable + columns={['Virtual Function Components', 'Compute Flavors']} + numOfIcons={0}> + {componentComputeAssociations.map( (association, index) => + <SelectActionTableRow key={association.componentId}> + <SelectActionTableCell + options={ + componentsList + .map(component => ({value: component.id, label: component.displayName}) ) + } + selected={association.componentId} + onChange={componentId => { + let newAssociations = [...componentComputeAssociations]; + newAssociations[index] = {...newAssociations[index], componentId}; + onDataChanged({componentComputeAssociations: newAssociations}); + }} + disabled={true}/> + <SelectActionTableCell + options={ + computesList + .filter(compute => compute.componentId === association.componentId) + .map(compute => ({value: compute.computeFlavorId, label: compute.name}) ) + } + selected={association.computeFlavorId} + onChange={computeFlavorId => { + let newAssociations = [...componentComputeAssociations]; + newAssociations[index] = {...newAssociations[index], computeFlavorId}; + onDataChanged({componentComputeAssociations: newAssociations}); + }} + disabled={isReadOnlyMode}/> + </SelectActionTableRow> + )} + </SelectActionTable> + </GridItem> + </GridSection> + </Form>} + </div> + ); + } + + validateName(value) { + const {data: {id = ''}, DFNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: DFNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Deployment flavor by the name \'' + value + '\' already exists. Deployment flavor name must be unique')}; + } + + submit(){ + let {isEdit, onCreate, onEdit, onClose, data} = this.props; + if (isEdit) { + onEdit(data); + } else { + onCreate(data); + } + onClose(); + } + + validate() { + this.props.onValidateForm(); + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js index e8091bf8d1..8806ffd0bf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js @@ -20,6 +20,7 @@ import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; import LandingPageView from './SoftwareProductLandingPageView.jsx'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {onboardingMethod} from '../SoftwareProductConstants.js'; export const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct; @@ -52,7 +53,8 @@ export const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreemen fullCategoryDisplayName }, isReadOnlyMode, - componentsList + componentsList, + isManual: currentSoftwareProduct.onboardingMethod === onboardingMethod.MANUAL }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx index 5fbf1b74b0..d3738e3ea4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -19,11 +19,10 @@ import Dropzone from 'react-dropzone'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; -import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; -import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; -import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; + +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; +import SoftwareProductComponentsList from '../components/SoftwareProductComponentsList.js'; const SoftwareProductPropType = React.PropTypes.shape({ name: React.PropTypes.string, @@ -47,7 +46,7 @@ const ComponentPropType = React.PropTypes.shape({ class SoftwareProductLandingPageView extends React.Component { state = { - localFilter: '', + fileName: '', dragging: false, files: [] @@ -67,13 +66,13 @@ class SoftwareProductLandingPageView extends React.Component { }; render() { - let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props; + let {currentSoftwareProduct, isReadOnlyMode, isManual, onDetailsSelect, componentsList} = this.props; return ( <div className='software-product-landing-wrapper'> <Dropzone className={classnames('software-product-landing-view', {'active-dragging': this.state.dragging})} - onDrop={files => this.handleImportSubmit(files, isReadOnlyMode)} - onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} + onDrop={files => this.handleImportSubmit(files, isReadOnlyMode, isManual)} + onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode, isManual)} onDragLeave={() => this.setState({dragging:false})} multiple={false} disableClick={true} @@ -84,68 +83,29 @@ class SoftwareProductLandingPageView extends React.Component { <div className='draggable-wrapper'> <div className='software-product-landing-view-top'> <div className='row'> - {this.renderProductSummary(currentSoftwareProduct)} - {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)} + <ProductSummary currentSoftwareProduct={currentSoftwareProduct} onDetailsSelect={onDetailsSelect} /> + {isManual ? + <div className='details-panel'/> + : this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)} </div> </div> </div> </Dropzone> - { - componentsList.length > 0 && this.renderComponents() - } + <SoftwareProductComponentsList + isReadOnlyMode={isReadOnlyMode} + componentsList={componentsList} + isManual={isManual} + currentSoftwareProduct={currentSoftwareProduct}/> </div> ); } - handleOnDragEnter(isReadOnlyMode) { - if (!isReadOnlyMode) { + handleOnDragEnter(isReadOnlyMode, isManual) { + if (!isReadOnlyMode && !isManual) { this.setState({dragging: true}); } } - renderProductSummary(currentSoftwareProduct) { - let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct; - let {onDetailsSelect} = this.props; - return ( - <div className='details-panel'> - <div className='software-product-landing-view-heading-title'>{i18n('Software Product Details')}</div> - <div - className='software-product-landing-view-top-block clickable' - onClick={() => onDetailsSelect(currentSoftwareProduct)}> - <div className='details-container'> - <div className='single-detail-section title-section'> - <div className='single-detail-section title-text'> - {name} - </div> - </div> - <div className='details-section'> - <div className='multiple-details-section'> - <div className='detail-col' > - <div className='title'>{i18n('Vendor')}</div> - <div className='description'>{vendorName}</div> - </div> - <div className='detail-col'> - <div className='title'>{i18n('Category')}</div> - <div className='description'>{fullCategoryDisplayName}</div> - </div> - <div className='detail-col'> - <div className='title extra-large'>{i18n('License Agreement')}</div> - <div className='description'> - {this.renderLicenseAgreement(licenseAgreementName)} - </div> - </div> - </div> - <div className='single-detail-section'> - <div className='title'>{i18n('Description')}</div> - <div className='description'>{description}</div> - </div> - </div> - </div> - </div> - </div> - ); - } - renderProductDetails(currentSoftwareProduct, isReadOnlyMode) { let {validationData} = currentSoftwareProduct; let {onAttachmentsSelect} = this.props; @@ -181,64 +141,8 @@ class SoftwareProductLandingPageView extends React.Component { ); } - renderComponents() { - const {localFilter} = this.state; - - return ( - <ListEditorView - title={i18n('Virtual Function Components')} - filterValue={localFilter} - placeholder={i18n('Filter Components')} - onFilter={value => this.setState({localFilter: value})} - twoColumns> - {this.filterList().map(component => this.renderComponentsListItem(component))} - </ListEditorView> - ); - } - - renderComponentsListItem(component) { - let {id: componentId, name, displayName, description = ''} = component; - let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; - return ( - <ListEditorItemView - key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} - className='list-editor-item-view' - onSelect={() => onComponentSelect({id, componentId})}> - <ListEditorItemViewField> - <div className='name'>{displayName}</div> - </ListEditorItemViewField> - <ListEditorItemViewField> - <div className='description'>{description}</div> - </ListEditorItemViewField> - </ListEditorItemView> - ); - } - - renderLicenseAgreement(licenseAgreementName) { - if (licenseAgreementName !== null && !licenseAgreementName) { - return (<div className='missing-license'><SVGIcon name='exclamation-triangle-full'/><div className='warning-text'>{i18n('Missing')}</div></div>); - } - return (licenseAgreementName); - } - - - filterList() { - let {componentsList = []} = this.props; - - let {localFilter} = this.state; - if (localFilter.trim()) { - const filter = new RegExp(escape(localFilter), 'i'); - return componentsList.filter(({displayName = '', description = ''}) => { - return escape(displayName).match(filter) || escape(description).match(filter); - }); - } - else { - return componentsList; - } - } - - handleImportSubmit(files, isReadOnlyMode) { - if (isReadOnlyMode) { + handleImportSubmit(files, isReadOnlyMode, isManual) { + if (isReadOnlyMode || isManual) { return; } if (files[0] && files[0].size) { @@ -280,4 +184,54 @@ class SoftwareProductLandingPageView extends React.Component { } } +const ProductSummary = ({currentSoftwareProduct, onDetailsSelect}) => { + let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct; + return ( + <div className='details-panel'> + <div className='software-product-landing-view-heading-title'>{i18n('Software Product Details')}</div> + <div + className='software-product-landing-view-top-block clickable' + onClick={() => onDetailsSelect(currentSoftwareProduct)}> + <div className='details-container'> + <div className='single-detail-section title-section'> + <div className='single-detail-section title-text'> + {name} + </div> + </div> + <div className='details-section'> + <div className='multiple-details-section'> + <div className='detail-col' > + <div className='title'>{i18n('Vendor')}</div> + <div className='description'>{vendorName}</div> + </div> + <div className='detail-col'> + <div className='title'>{i18n('Category')}</div> + <div className='description'>{fullCategoryDisplayName}</div> + </div> + <div className='detail-col'> + <div className='title extra-large'>{i18n('License Agreement')}</div> + <div className='description'> + <LicenseAgreement licenseAgreementName={licenseAgreementName}/> + </div> + </div> + </div> + <div className='single-detail-section'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> + </div> + </div> + </div> + </div> + ); +}; + + +const LicenseAgreement = ({licenseAgreementName}) => { + if (!licenseAgreementName) { + return (<div className='missing-license'><SVGIcon name='exclamationTriangleFull'/><div className='warning-text'>{i18n('Missing')}</div></div>); + } + return <div>{licenseAgreementName}</div>; +}; + export default SoftwareProductLandingPageView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js index 66926ce4b3..afd6331324 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js @@ -42,7 +42,7 @@ const mapActionsToProps = (dispatch, {softwareProductId}) => { onDeleteProcess: (process, version) => dispatch({ type: modalActionTypes.GLOBAL_MODAL_WARNING, data:{ - msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + msg: i18n(`Are you sure you want to delete "${process.name}"?`), onConfirmed: ()=> SoftwareProductProcessesActionHelper.deleteProcess(dispatch, {process, softwareProductId, version}) } diff --git a/openecomp-ui/src/sdc-app/punch-outs.js b/openecomp-ui/src/sdc-app/punch-outs.js index 78b64da846..125050bfdb 100644 --- a/openecomp-ui/src/sdc-app/punch-outs.js +++ b/openecomp-ui/src/sdc-app/punch-outs.js @@ -13,6 +13,7 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ +import 'sdc-ui/css/style.css'; import '../../resources/scss/onboarding.scss'; import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; |