diff options
Diffstat (limited to 'openecomp-ui/src')
295 files changed, 13677 insertions, 7958 deletions
diff --git a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx index f2ec1582f3..0759f2c28d 100644 --- a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx +++ b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx @@ -1,9 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component} from 'react'; import ListGroupItem from 'react-bootstrap/lib/ListGroupItem.js'; -import ListGroup from 'react-bootstrap/lib/ListGroup.js'; -import Panel from 'react-bootstrap/lib/Panel.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; - +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import {Collapse} from 'react-bootstrap'; /** * parsing and showing the following Java Response object * @@ -31,103 +46,117 @@ class SubmitErrorResponse extends Component { render() { - let {validationResponse} = this.props; + let {validationResponse : {vspErrors, licensingDataErrors, questionnaireValidationResult, uploadDataErrors}} = this.props; return ( <div className='submit-error-response-view'> - {validationResponse.vspErrors && this.renderVspErrors(validationResponse.vspErrors)} - {validationResponse.licensingDataErrors && this.renderVspErrors(validationResponse.licensingDataErrors)} - {validationResponse.compilationErrors && this.renderCompilationErrors(validationResponse.compilationErrors)} - {validationResponse.uploadDataErrors && this.renderUploadDataErrors(validationResponse.uploadDataErrors)} - {validationResponse.questionnaireValidationResult && this.renderQuestionnaireValidationResult(validationResponse.questionnaireValidationResult)} + {vspErrors && this.renderVspErrors(vspErrors)} + {licensingDataErrors && this.renderVspErrors(licensingDataErrors)} + {questionnaireValidationResult && this.renderComponentsErrors(questionnaireValidationResult)} + {uploadDataErrors && this.renderUploadDataErrors(uploadDataErrors)} </div> ); } - renderVspErrors(vspErrors) { + renderVspErrors(errors) { return ( - <Panel header={i18n('VSP Errors')} collapsible>{this.parseErrorCodeCollection(vspErrors)}</Panel> + <ErrorBlock errorType={i18n('VSP Errors')}> + <div> + {errors.length && errors.map(error=>{return (<ErrorMessage error={error.message}/>);})} + </div> + </ErrorBlock> ); } - renderLicensingDataErrors(licensingDataErrors) { + + renderComponentsErrors(errors) { return ( - <Panel - header={i18n('Licensing Data Errors')} - collapsible>{this.parseErrorCodeCollection(licensingDataErrors)} - </Panel> + <ErrorBlock errorType={i18n('Components Errors')}> + <div> + {errors.validationData.length && errors.validationData.map(item =>{ return (<ComponentError item={item}/>);})} + </div> + </ErrorBlock> ); } renderUploadDataErrors(uploadDataErrors) { return ( - <Panel - header={i18n('Upload Data Errors')} - collapsible>{this.parseMapOfErrorMessagesList(uploadDataErrors)} - </Panel> + <ErrorBlock errorType={i18n('Upload Data Errors')}> + <div> + <UploadErrorList items={uploadDataErrors}/> + </div> + </ErrorBlock> ); } +} - renderCompilationErrors(compilationErrors) { - return ( - <Panel - header={i18n('Compilation Errors')} - collapsible>{this.parseMapOfErrorMessagesList(compilationErrors)} - </Panel> - ); - } - parseErrorCodeCollection(errors) { - return ( - <ListGroup>{errors.map(error => - <ListGroupItem className='error-code-list-item'> - <div><span>{i18n('Category: ')}</span>{error.category}</div> - <div><span>{i18n('Message: ')}</span>{error.message}</div> - </ListGroupItem> - )}</ListGroup> - ); - } +const ComponentError = ({item}) => { + let i = 0; + return ( + <div> + <div className='component-name-header'>{item.entityName}</div> + {item.errors.map(error => {return(<ErrorMessage key={i++} error={error}/>);})} + </div> + ); +}; - parseMapOfErrorMessagesList(errorMap) { - return ( - <ListGroup> - {Object.keys(errorMap).map(errorStringKey => - <Panel header={errorStringKey} collapsible> - <ListGroup>{errorMap[errorStringKey].map(error => - <ListGroupItem className='error-code-list-item'> - <div><span>{i18n('Level: ')}</span>{error.level}</div> - <div><span>{i18n('Message: ')}</span>{error.message}</div> - </ListGroupItem> - )}</ListGroup> - </Panel> - )} - </ListGroup> - ); +function* entries(obj) { + for (let key of Object.keys(obj)) { + yield {header: key, list: obj[key]}; } +} +const UploadErrorList = ({items}) => { + let generator = entries(items); + + let errors = []; + let i = 0; + for (let item of generator) {errors.push( + <div> + <div className='component-name-header'>{item.header}</div> + {item.list.map(error => <ErrorMessage key={i++} warning={error.level === 'WARNING'} error={error.message}/> )} + </div> + );} + return ( + <div> + {errors} + </div> + ); +}; + +class ErrorBlock extends React.Component { + state = { + collapsed: false + }; - renderQuestionnaireValidationResult(questionnaireValidationResult) { - if (!questionnaireValidationResult.valid) { - return this.parseAndRenderCompositionEntityValidationData(questionnaireValidationResult.validationData); - } - } - - parseAndRenderCompositionEntityValidationData(validationData) { - let {entityType, entityId, errors = [], subEntitiesValidationData = []} = validationData; + render() { + let {errorType, children} = this.props; return ( - <ListGroup> - <Panel header={`${entityType}: ${entityId}`} collapsible> - <ListGroup>{errors.map(error => - <ListGroupItem className='error-code-list-item'> - <div>{error}</div> - </ListGroupItem> - )}</ListGroup> - {subEntitiesValidationData.map(subValidationData => this.parseAndRenderCompositionEntityValidationData(subValidationData))} - </Panel> - </ListGroup> + <div className='error-block'> + <ErrorHeader collapsed={this.state.collapsed} onClick={()=>{this.setState({collapsed: !this.state.collapsed});}} errorType={errorType}/> + <Collapse in={this.state.collapsed}> + {children} + </Collapse> + </div> ); } - - } +const ErrorHeader = ({errorType, collapsed, onClick}) => { + return( + <div onClick={onClick} className='error-block-header'> + <SVGIcon iconClassName={collapsed ? '' : 'right' } name='chevron-down'/> + {errorType} + </div> + ); +}; + +const ErrorMessage = ({error, warning}) => { + return ( + <ListGroupItem className='error-code-list-item'> + <Icon image={warning ? 'warning' : 'error'} label={error}/> + </ListGroupItem> + ); +}; + export default SubmitErrorResponse; diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js new file mode 100644 index 0000000000..f7354f96e2 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.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 {connect} from 'react-redux'; +import ActivityLogView from './ActivityLogView.jsx'; + +export const mapStateToProps = ({licenseModel: {activityLog}}) => { + + let activities = activityLog; + return { + activities + }; +}; + +export default connect(mapStateToProps, undefined, null, {withRef: true})(ActivityLogView); diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js new file mode 100644 index 0000000000..01a27abbc5 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js @@ -0,0 +1,31 @@ +/*! + * 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 ActivityLogConstants from './ActivityLogConstants.js'; + + +function baseUrl(itemId, versionId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/activity-logs/${itemId}/versions/${versionId}`; +} + +export default { + + fetchActivityLog(dispatch, {itemId, versionId}){ + return RestAPIUtil.fetch(baseUrl(itemId, versionId)).then(response => dispatch({type: ActivityLogConstants.ACTIVITY_LOG_UPDATED, response})); + } +}; diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js new file mode 100644 index 0000000000..69faf7cbb6 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js @@ -0,0 +1,23 @@ +/*! + * 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 default keyMirror({ + + ACTIVITY_LOG_UPDATED: null + +}); + diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js new file mode 100644 index 0000000000..fc3dfa1515 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.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 ActivityLogConstants from './ActivityLogConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case ActivityLogConstants.ACTIVITY_LOG_UPDATED: + return [...action.response.results]; + } + + return state; +}; diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx new file mode 100644 index 0000000000..6ff3c806a8 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx @@ -0,0 +1,124 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import 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 i18n from 'nfvo-utils/i18n/i18n.js'; + +function ActivityLogSortableCellHeader({isHeader, data, isDes, onSort}) { + if (isHeader) { + return ( + <span className='date-header' onClick={onSort}> + <span>{data}</span> + <span className={`header-sort-arrow ${isDes ? 'up' : 'down'}`}></span> + </span> + ); + } + return ( + <span className='date-cell'> + <span>{i18n.dateNormal(data, { + year: 'numeric', month: 'numeric', day: 'numeric' + })}</span> + <span>{i18n.dateNormal(data, { + hour: 'numeric', minute: 'numeric', + hour12: true + })}</span> + </span> + ); +} + +function ActivityLogStatus({status, isHeader}) { + if (isHeader) { + return <span>{status}</span>; + } + let {message, success} = status; + return ( + <span> + <span className={`status-icon ${success}`}>{`${success ? i18n('Success') : i18n('Failure')}`}</span> + {success && <SVGIcon name='check-circle'/>} + {!success && <OverlayTrigger placement='bottom' overlay={<Tooltip className='activity-log-message-tooltip' id={'activity-log-message-tooltip'}> + <div className='message-block'>{message}</div> + </Tooltip>}> + <span className='message-further-info-icon'>{'?'}</span> + </OverlayTrigger>} + </span> + ); +} + +export function ActivityListItem({activity, isHeader, isDes, onSort}) { + let {type, timestamp, comment, user, status} = activity; + return ( + <li className={`activity-list-item ${isHeader ? 'header' : ''}`} data-test-id='activity-list-item'> + <div className='table-cell activity-date' data-test-id='activity-date'><ActivityLogSortableCellHeader isHeader={isHeader} data={timestamp} isDes={isDes} onSort={onSort}/></div> + <div className='table-cell activity-action' data-test-id='activity-action'>{type}</div> + <div className='table-cell activity-comment' title={comment} data-test-id='activity-comment'><span>{comment}</span></div> + <div className='table-cell activity-username' data-test-id='activity-username'>{user}</div> + <div className='table-cell activity-status' data-test-id='activity-status'><ActivityLogStatus isHeader={isHeader} status={status}/></div> + </li> + ); +} + +class ActivityLogView extends Component { + + state = { + localFilter: '', + sortDescending: true + }; + + render() { + return ( + <div className='activity-log-view'> + <ListEditorView + title={i18n('Activity Log')} + filterValue={this.state.localFilter} + onFilter={filter => this.setState({localFilter: filter})}> + <ActivityListItem + isHeader={true} + activity={{timestamp: 'Date', type: 'Action', comment: 'Comment', user: 'Username', status: 'Status'}} + isDes={this.state.sortDescending} + onSort={() => this.setState({sortDescending: !this.state.sortDescending})}/> + {this.sortActivities(this.filterActivities(), this.state.sortDescending).map(activity => <ActivityListItem key={activity.id} activity={activity}/>)} + </ListEditorView> + </div> + ); + } + + filterActivities() { + let {activities} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return activities.filter(({user = '', comment = '', type = ''}) => escape(user).match(filter) || escape(comment).match(filter) || escape(type).match(filter)); + } + else { + return activities; + } + } + + sortActivities(activities) { + if (this.state.sortDescending) { + return activities.sort((a, b) => a.timestamp - b.timestamp); + } + else { + return activities.reverse(); + } + } + +} + +export default ActivityLogView; diff --git a/openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx b/openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx deleted file mode 100644 index cc971c608c..0000000000 --- a/openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import Button from 'react-bootstrap/lib/Button.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Modal from 'nfvo-components/modal/Modal.jsx'; - -let typeClass = { - 'default': 'primary', - error: 'danger', - warning: 'warning', - success: 'success' -}; - - -class ConfirmationModalView extends React.Component { - - static propTypes = { - show: React.PropTypes.bool, - type: React.PropTypes.oneOf(['default', 'error', 'warning', 'success']), - msg: React.PropTypes.node, - title: React.PropTypes.string, - confirmationDetails: React.PropTypes.object, - confirmationButtonText: React.PropTypes.string, - - }; - - static defaultProps = { - show: false, - type: 'warning', - title: 'Warning', - msg: '', - confirmationButtonText: i18n('Delete') - }; - - render() { - let {title, type, msg, show, confirmationButtonText} = this.props; - - return( - <Modal show={show} className={`notification-modal ${typeClass[type]}`}> - <Modal.Header> - <Modal.Title>{title}</Modal.Title> - </Modal.Header> - <Modal.Body>{msg}</Modal.Body> - <Modal.Footer> - <Button bsStyle={typeClass[type]} onClick={() => this.props.onDeclined(this.props.confirmationDetails)}>{i18n('Cancel')}</Button> - <Button bsStyle={typeClass[type]} onClick={() => this.props.onConfirmed(this.props.confirmationDetails)}>{confirmationButtonText}</Button> - </Modal.Footer> - </Modal> - ); - }; -} - -export default ConfirmationModalView; diff --git a/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx b/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx index 4a106b5ff4..2a0b7d4d2a 100644 --- a/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx +++ b/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import classnames from 'classnames'; @@ -7,10 +22,17 @@ import NavigationSideBar from 'nfvo-components/panel/NavigationSideBar.jsx'; export default class TabulatedEditor extends React.Component { render() { - const {versionControllerProps, navigationBarProps, onToggle, onVersionSwitching, onCreate, onSave, onClose, onVersionControllerAction, onNavigate, children} = this.props; + const {navigationBarProps, onToggle, onVersionSwitching, onCreate, onSave, onClose, onVersionControllerAction, onNavigate, children, meta} = this.props; + let {versionControllerProps} = this.props; const {className = ''} = React.Children.only(children).props; const child = this.prepareChild(); + if(onClose) { + versionControllerProps = { + ...versionControllerProps, + onClose: () => onClose(versionControllerProps) + }; + } return ( <div className='software-product-view'> <div className='software-product-navigation-side-bar'> @@ -19,11 +41,10 @@ export default class TabulatedEditor extends React.Component { <div className='software-product-landing-view-right-side flex-column'> <VersionController {...versionControllerProps} - onVersionSwitching={version => onVersionSwitching(version)} - callVCAction={onVersionControllerAction} + onVersionSwitching={version => onVersionSwitching(version, meta)} + callVCAction={(action, version) => onVersionControllerAction(action, version, meta)} onCreate={onCreate && this.handleCreate} - onSave={onSave && this.handleSave} - onClose={() => onClose(versionControllerProps)}/> + onSave={onSave && this.handleSave}/> <div className={classnames('content-area', `${className}`)}> { child diff --git a/openecomp-ui/src/nfvo-components/grid/GridItem.jsx b/openecomp-ui/src/nfvo-components/grid/GridItem.jsx new file mode 100644 index 0000000000..8819ab78a3 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/grid/GridItem.jsx @@ -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 React from 'react'; + +const GridItem = ({colSpan = 1, children, stretch = false}) => ( + <div className={`grid-col-${colSpan}`}> + <div className={`grid-item${stretch ? '-stretch' : ''}`}> + {children} + </div> + </div> +); + +export default GridItem; diff --git a/openecomp-ui/src/nfvo-components/grid/GridSection.jsx b/openecomp-ui/src/nfvo-components/grid/GridSection.jsx new file mode 100644 index 0000000000..175b3ee082 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/grid/GridSection.jsx @@ -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 React from 'react'; + +const GridSection = ({title, children, titleClassName}) => { + return ( + <div className='grid-section'> + {title && <div className={`section-title ${titleClassName || ''}`}>{title}</div>} + <div className='grid-items'> + {children} + </div> + </div> + ); +}; + +GridSection.propTypes = { + title: React.PropTypes.string, +}; + +export default GridSection; diff --git a/openecomp-ui/src/nfvo-components/icon/Icon.jsx b/openecomp-ui/src/nfvo-components/icon/Icon.jsx new file mode 100644 index 0000000000..125577664b --- /dev/null +++ b/openecomp-ui/src/nfvo-components/icon/Icon.jsx @@ -0,0 +1,45 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, { Component, PropTypes } from 'react'; + + +export default class Icon extends Component { + + static propTypes = { + image: PropTypes.string.isRequired, + onClick: PropTypes.func, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + className: PropTypes.string, + iconClassName: PropTypes.string + }; + + static defaultProps = { + label: '', + className: '', + iconClassName: '' + }; + + render() { + let {image, onClick, label, className, iconClassName, ...other} = this.props; + let classes = `icon-component ${className} ${onClick ? 'clickable' : ''}`; + return ( + <div {...other} onClick={onClick} className={classes}> + <span className={`icon ${image} ${iconClassName}`}></span> + <span className='icon-label'>{label}</span> + </div> + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx b/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx new file mode 100644 index 0000000000..dd165fb52c --- /dev/null +++ b/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx @@ -0,0 +1,54 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {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 new file mode 100644 index 0000000000..6675670cea --- /dev/null +++ b/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js @@ -0,0 +1,50 @@ +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 3ac3fcad28..e2ee40fcd2 100644 --- a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx @@ -1,77 +1,115 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import classnames from 'classnames'; -import Input from 'react-bootstrap/lib/Input'; +import ReactDOM from 'react-dom'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; +const ExpandableInputClosed = ({iconType, onClick}) => ( + <SVGIcon className='expandable-input-wrapper closed' name={iconType} onClick={onClick} /> +); -class ExpandableInput extends React.Component { - constructor(props){ - super(props); - this.state = {showInput: false, value: ''}; - this.toggleInput = this.toggleInput.bind(this); - this.handleFocus = this.handleFocus.bind(this); - this.handleInput = this.handleInput.bind(this); - this.handleClose = this.handleClose.bind(this); +class ExpandableInputOpened extends React.Component { + componentDidMount(){ + this.rawDomNode = ReactDOM.findDOMNode(this.searchInputNode.inputWrapper); + this.rawDomNode.focus(); } - toggleInput(){ - if (!this.state.showInput){ - this.searchInputNode.refs.input.focus(); - } else { - this.setState({showInput: false}); + componentWillReceiveProps(newProps){ + if (!newProps.value){ + if (!(document.activeElement === this.rawDomNode)){ + this.props.handleBlur(); + } } } - handleInput(e){ - let {onChange} = this.props; + handleClose(){ + this.props.onChange(''); + this.rawDomNode.focus(); + } - this.setState({value: e.target.value}); - onChange(e); + handleKeyDown(e){ + if (e.key === 'Escape'){ + e.preventDefault(); + if (this.props.value) { + this.handleClose(); + } else { + this.rawDomNode.blur(); + } + }; } - handleClose(){ - this.handleInput({target: {value: ''}}); - this.searchInputNode.refs.input.focus(); + render() { + let {iconType, value, onChange, handleBlur} = this.props; + return ( + <div className='expandable-input-wrapper opened' key='expandable'> + <Input + type='text' + value={value} + ref={(input) => this.searchInputNode = input} + className='expandable-active' + groupClassName='expandable-input-control' + onChange={e => onChange(e)} + onKeyDown={e => this.handleKeyDown(e)} + onBlur={handleBlur}/> + {value && <SVGIcon onClick={() => this.handleClose()} name='close' />} + {!value && <SVGIcon name={iconType} onClick={handleBlur}/>} + </div> + ); } +} + +class ExpandableInput extends React.Component { - handleFocus(){ - if (!this.state.showInput){ - this.setState({showInput: true}); + static propTypes = { + iconType: React.PropTypes.string, + onChange: React.PropTypes.func, + value: React.PropTypes.string + }; + + state = {showInput: false}; + + closeInput(){ + if (!this.props.value) { + this.setState({showInput: false}); } } getValue(){ - return this.state.value; + return this.props.value; } render(){ - let {iconType} = this.props; - - let inputClasses = classnames({ - 'expandable-active': this.state.showInput, - 'expandable-not-active': !this.state.showInput - }); - - let iconClasses = classnames( - 'expandable-icon', - {'expandable-icon-active': this.state.showInput} - ); - + let {iconType, value, onChange = false} = this.props; return ( - <div className='expandable-input-wrapper'> - <Input - type='text' - value={this.state.value} - ref={(input) => this.searchInputNode = input} - className={inputClasses} - groupClassName='expandable-input-control' - onChange={e => this.handleInput(e)} - onFocus={this.handleFocus}/> - {this.state.showInput && this.state.value && <FontAwesome onClick={this.handleClose} name='close' className='expandable-close-button'/>} - {!this.state.value && <FontAwesome onClick={this.toggleInput} name={iconType} className={iconClasses}/>} + <div className='expandable-input-top'> + {this.state.showInput && + <ExpandableInputOpened + key='open' + iconType={iconType} + onChange={onChange} + value={value} + handleKeyDown={(e) => this.handleKeyDown(e)} + handleBlur={() => this.closeInput()}/> + } + {!this.state.showInput && <ExpandableInputClosed key='closed' iconType={iconType} onClick={() => this.setState({showInput: true})} />} </div> - ); + ); } } + export default ExpandableInput; diff --git a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx index 1036ac41c3..03c727379e 100644 --- a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ /** * The HTML structure here is aligned with bootstrap HTML structure for form elements. * In this way we have proper styling and it is aligned with other form elements on screen. @@ -20,8 +35,9 @@ class SelectInput extends Component { render() { let {label, value, ...other} = this.props; + const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; return ( - <div className='validation-input-wrapper dropdown-multi-select'> + <div {...dataTestId} className='validation-input-wrapper dropdown-multi-select'> <div className='form-group'> {label && <label className='control-label'>{label}</label>} <Select ref='_myInput' onChange={value => this.onSelectChanged(value)} {...other} value={value} /> diff --git a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx index 873d3ded65..7bbafa3696 100644 --- a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; export default diff --git a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx index 171bead9bb..c60d6f777e 100644 --- a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx +++ b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx @@ -1,6 +1,21 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import Input from 'react-bootstrap/lib/Input.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; class DualListboxView extends React.Component { @@ -30,37 +45,32 @@ class DualListboxView extends React.Component { state = { availableListFilter: '', - selectedValuesListFilter: '' - }; - - static contextTypes = { - isReadOnlyMode: React.PropTypes.bool + selectedValuesListFilter: '', + selectedValues: [] }; render() { - let {availableList, selectedValuesList, filterTitle} = this.props; + let {availableList, selectedValuesList, filterTitle, isReadOnlyMode} = this.props; let {availableListFilter, selectedValuesListFilter} = this.state; - let isReadOnlyMode = this.context.isReadOnlyMode; let unselectedList = availableList.filter(availableItem => !selectedValuesList.find(value => value === availableItem.id)); let selectedList = availableList.filter(availableItem => selectedValuesList.find(value => value === availableItem.id)); selectedList = selectedList.sort((a, b) => selectedValuesList.indexOf(a.id) - selectedValuesList.indexOf(b.id)); - return ( <div className='dual-list-box'> {this.renderListbox(filterTitle.left, unselectedList, { value: availableListFilter, ref: 'availableListFilter', disabled: isReadOnlyMode, - onChange: () => this.setState({availableListFilter: this.refs.availableListFilter.getValue()}) - }, {ref: 'availableValues', disabled: isReadOnlyMode})} + onChange: (value) => this.setState({availableListFilter: value}) + }, {ref: 'availableValues', disabled: isReadOnlyMode, testId: 'available',})} {this.renderOperationsBar(isReadOnlyMode)} {this.renderListbox(filterTitle.right, selectedList, { value: selectedValuesListFilter, ref: 'selectedValuesListFilter', disabled: isReadOnlyMode, - onChange: () => this.setState({selectedValuesListFilter: this.refs.selectedValuesListFilter.getValue()}) - }, {ref: 'selectedValues', disabled: isReadOnlyMode})} + onChange: (value) => this.setState({selectedValuesListFilter: value}) + }, {ref: 'selectedValues', disabled: isReadOnlyMode, testId: 'selected'})} </div> ); } @@ -69,21 +79,25 @@ class DualListboxView extends React.Component { let regExFilter = new RegExp(escape(filterProps.value), 'i'); let matchedItems = list.filter(item => item.name.match(regExFilter)); let unMatchedItems = list.filter(item => !item.name.match(regExFilter)); - - return ( <div className='dual-search-multi-select-section'> <p>{filterTitle}</p> <div className='dual-text-box-search search-wrapper'> - <Input name='search-input-control' type='text' groupClassName='search-input-control' {...filterProps}/> - <FontAwesome name='search' className='search-icon'/> + <Input data-test-id={`${props.testId}-search-input`} + name='search-input-control' type='text' + groupClassName='search-input-control' + {...filterProps}/> + <SVGIcon name='search' className='search-icon'/> </div> <Input multiple + onChange={(event) => this.onSelectItems(event.target.selectedOptions)} groupClassName='dual-list-box-multi-select' type='select' name='dual-list-box-multi-select' - {...props}> + data-test-id={`${props.testId}-select-input`} + disabled={props.disabled} + ref={props.ref}> {matchedItems.map(item => this.renderOption(item.id, item.name))} {matchedItems.length && unMatchedItems.length && <option style={{pointerEvents: 'none'}}>--------------------</option>} {unMatchedItems.map(item => this.renderOption(item.id, item.name))} @@ -92,6 +106,11 @@ class DualListboxView extends React.Component { ); } + onSelectItems(selectedOptions) { + let selectedValues = Object.keys(selectedOptions).map((k) => selectedOptions[k].value); + this.setState({selectedValues}); + } + renderOption(value, name) { return (<option className='dual-list-box-multi-select-text' key={value} value={value}>{name}</option>); } @@ -107,17 +126,19 @@ class DualListboxView extends React.Component { ); } - renderOperationBarButton(onClick, fontAwesomeIconName){ - return (<div className='dual-list-option' onClick={onClick}><FontAwesome name={fontAwesomeIconName}/></div>); + renderOperationBarButton(onClick, iconName){ + return (<div className='dual-list-option' data-test-id={`operation-icon-${iconName}`} onClick={onClick}><SVGIcon name={iconName}/></div>); } addToSelectedList() { - this.props.onChange(this.props.selectedValuesList.concat(this.refs.availableValues.getValue())); + this.props.onChange(this.props.selectedValuesList.concat(this.state.selectedValues)); + this.setState({selectedValues: []}); } removeFromSelectedList() { - const selectedValues = this.refs.selectedValues.getValue(); + const selectedValues = this.state.selectedValues; this.props.onChange(this.props.selectedValuesList.filter(value => !selectedValues.find(selectedValue => selectedValue === value))); + this.setState({selectedValues: []}); } addAllToSelectedList() { diff --git a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx index 5daaffea41..e8aadc4357 100644 --- a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx +++ b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import classNames from 'classnames'; @@ -13,15 +28,21 @@ class InputOptions extends React.Component { title: React.PropTypes.string })), isEnabledOther: React.PropTypes.bool, - title: React.PropTypes.string, + label: React.PropTypes.string, selectedValue: React.PropTypes.string, - multiSelectedEnum: React.PropTypes.array, + multiSelectedEnum: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.array + ]), selectedEnum: React.PropTypes.string, otherValue: React.PropTypes.string, onEnumChange: React.PropTypes.func, onOtherChange: React.PropTypes.func, + onBlur: React.PropTypes.func, isRequired: React.PropTypes.bool, - isMultiSelect: React.PropTypes.bool + isMultiSelect: React.PropTypes.bool, + hasError: React.PropTypes.bool, + disabled: React.PropTypes.bool }; @@ -41,7 +62,7 @@ class InputOptions extends React.Component { render() { let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, hasError, validations, children} = this.props; - + const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; let currentMultiSelectedEnum = []; let currentSelectedEnum = ''; let {otherInputDisabled} = this.state; @@ -54,14 +75,18 @@ class InputOptions extends React.Component { else if(selectedEnum){ currentSelectedEnum = selectedEnum; } + if (!onBlur) { + onBlur = () => {}; + } let isReadOnlyMode = this.context.isReadOnlyMode; return( - <div className={classNames('form-group', {'required' : validations.required , 'has-error' : hasError})}> + <div className={classNames('form-group', {'required' : (validations && validations.required) || isRequired, 'has-error' : hasError})}> {label && <label className='control-label'>{label}</label>} {isMultiSelect && otherInputDisabled ? <Select + {...dataTestId} ref='_myInput' value={currentMultiSelectedEnum} className='options-input' @@ -74,18 +99,18 @@ class InputOptions extends React.Component { multi/> : <div className={classNames('input-options',{'has-error' : hasError})}> <select + {...dataTestId} ref={'_myInput'} label={label} className='form-control input-options-select' value={currentSelectedEnum} - style={{'width' : otherInputDisabled ? '100%' : '95px'}} + style={{'width' : otherInputDisabled ? '100%' : '100px'}} onBlur={() => onBlur()} disabled={isReadOnlyMode || Boolean(this.props.disabled)} onChange={ value => this.enumChanged(value)} type='select'> - {values && values.length && values.map(val => this.renderOptions(val))} + {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))} {onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>} - {children} </select> {!otherInputDisabled && <div className='input-options-separator'/>} @@ -104,9 +129,9 @@ class InputOptions extends React.Component { ); } - renderOptions(val){ - return( - <option key={val.enum} value={val.enum}>{val.title}</option> + renderOptions(val, index){ + return ( + <option key={index} value={val.enum}>{val.title}</option> ); } @@ -154,9 +179,9 @@ class InputOptions extends React.Component { enumChanged() { let enumValue = this.refs._myInput.value; - let {onEnumChange, isMultiSelect, onChange} = this.props; + let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props; this.setState({ - otherInputDisabled: enumValue !== other.OTHER + otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER }); let value = isMultiSelect ? [enumValue] : enumValue; @@ -169,7 +194,7 @@ class InputOptions extends React.Component { } multiSelectEnumChanged(enumValue) { - let {onEnumChange} = this.props; + let {onEnumChange, onOtherChange} = this.props; let selectedValues = enumValue.map(enumVal => { return enumVal.value; }); @@ -182,7 +207,7 @@ class InputOptions extends React.Component { } this.setState({ - otherInputDisabled: !selectedValues.includes(i18n(other.OTHER)) + otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER)) }); onEnumChange(selectedValues); } diff --git a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx new file mode 100644 index 0000000000..47922f86a0 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx @@ -0,0 +1,114 @@ +/*! + * 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 ValidationButtons from './ValidationButtons.jsx'; + +class Form extends React.Component { + + static defaultProps = { + hasButtons : true, + onSubmit : null, + onReset : null, + labledButtons: true, + onValidChange : null, + isValid: true + }; + + static propTypes = { + isValid : React.PropTypes.bool, + formReady : React.PropTypes.bool, + isReadOnlyMode : React.PropTypes.bool, + hasButtons : React.PropTypes.bool, + onSubmit : React.PropTypes.func, + onReset : React.PropTypes.func, + labledButtons: React.PropTypes.bool, + onValidChange : React.PropTypes.func, + onValidityChanged: React.PropTypes.func, + onValidateForm: React.PropTypes.func + }; + + constructor(props) { + super(props); + } + + + render() { + // eslint-disable-next-line no-unused-vars + let {isValid, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, onDataChanged, children, ...formProps} = this.props; + return ( + <form {...formProps} ref={(form) => this.form = form} onSubmit={event => this.handleFormValidation(event)}> + <div className='validation-form-content'> + <fieldset disabled={isReadOnlyMode}> + {children} + </fieldset> + </div> + {hasButtons && <ValidationButtons labledButtons={labledButtons} ref={(buttons) => this.buttons = buttons} isReadOnlyMode={isReadOnlyMode}/>} + </form> + ); + } + + handleFormValidation(event) { + event.preventDefault(); + if (this.props.onValidateForm && !this.props.formReady){ + return this.props.onValidateForm(); + } else { + return this.handleFormSubmit(event); + } + } + handleFormSubmit(event) { + if (event) { + event.preventDefault(); + } + if(this.props.onSubmit) { + return this.props.onSubmit(event); + } + } + + componentDidMount() { + if (this.props.hasButtons) { + this.buttons.setState({isValid: this.props.isValid}); + } + } + + + + componentDidUpdate(prevProps) { + // only handling this programatically if the validation of the form is done outside of the view + // (example with a form that is dependent on the state of other forms) + if (prevProps.isValid !== this.props.isValid) { + if (this.props.hasButtons) { + this.buttons.setState({isValid: this.props.isValid}); + } + // callback in case form is part of bigger picture in view + if (this.props.onValidChange) { + this.props.onValidChange(this.props.isValid); + } + + // TODO - maybe this has to be part of componentWillUpdate + if(this.props.onValidityChanged) { + this.props.onValidityChanged(this.props.isValid); + } + } + if (this.props.formReady) { // if form validation succeeded -> continue with submit + this.handleFormSubmit(); + } + } + +} + + +export default Form; diff --git a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx new file mode 100644 index 0000000000..59c35d7993 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx @@ -0,0 +1,180 @@ +/*! + * 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 ReactDOM from 'react-dom'; +import classNames from 'classnames'; +import Checkbox from 'react-bootstrap/lib/Checkbox.js'; +import Radio from 'react-bootstrap/lib/Radio.js'; +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'; + +class Input extends React.Component { + + state = { + 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'; + if (disabled) { + wrapperClassName += ' disabled'; + } + return( + <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') && + <FormControl + bsClass={'form-control input-options-other'} + onChange={(e) => this.onChange(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + onBlur={onBlur} + onKeyDown={onKeyDown} + value={value || ''} + inputRef={(input) => this.input = input} + type={type} + data-test-id={this.props['data-test-id']}/>} + + {type === 'textarea' && + <FormControl + className='form-control input-options-other' + disabled={isReadOnlyMode || Boolean(disabled)} + value={value || ''} + onBlur={onBlur} + onKeyDown={onKeyDown} + componentClass={type} + onChange={(e) => this.onChange(e)} + inputRef={(input) => this.input = input} + data-test-id={this.props['data-test-id']}/>} + + {type === 'checkbox' && + <Checkbox + className={classNames({'required' : isRequired , 'has-error' : !isValid})} + onChange={(e)=>this.onChangeCheckBox(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + checked={value} + data-test-id={this.props['data-test-id']}>{label}</Checkbox>} + + {type === 'radio' && + <Radio name={name} + checked={checked} + disabled={isReadOnlyMode || Boolean(disabled)} + value={value} + onChange={(e)=>this.onChangeRadio(e)} + data-test-id={this.props['data-test-id']}>{label}</Radio>} + {type === 'select' && + <FormControl onClick={ (e) => this.optionSelect(e) } + componentClass={type} + inputRef={(input) => this.input = input} + name={name} {...inputProps} + data-test-id={this.props['data-test-id']}/>} + </FormGroup> + { this.renderErrorOverlay() } + </div> + ); + } + + getValue() { + return this.props.type !== 'select' ? this.state.value : this.state.selectedValues; + } + + getChecked() { + return this.state.checked; + } + + optionSelect(e) { + let selectedValues = []; + if (e.target.value) { + selectedValues.push(e.target.value); + } + this.setState({ + selectedValues + }); + } + + onChange(e) { + const {onChange, type} = this.props; + let value = e.target.value; + if (type === 'number') { + value = Number(value); + } + this.setState({ + value + }); + onChange(value); + } + + onChangeCheckBox(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(e.target.checked); + } + + onChangeRadio(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(this.state.value); + } + + focus() { + ReactDOM.findDOMNode(this.input).focus(); + } + + renderErrorOverlay() { + let position = 'right'; + const {errorText = '', isValid = true, type, overlayPos} = this.props; + + if (overlayPos) { + position = overlayPos; + } + else if (type === 'text' + || type === 'email' + || type === 'number' + || type === 'password') { + position = 'bottom'; + } + + return ( + <Overlay + show={!isValid} + placement={position} + target={() => { + let target = ReactDOM.findDOMNode(this.input); + return target.offsetParent ? target : undefined; + }} + container={this}> + <Tooltip + id={`error-${errorText.replace(' ', '-')}`} + className='validation-error-message'> + {errorText} + </Tooltip> + </Overlay> + ); + } + +} +export default Input; diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx new file mode 100644 index 0000000000..6e54254eb0 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx @@ -0,0 +1,279 @@ +/*! + * 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 ReactDOM from 'react-dom'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classNames from 'classnames'; +import Select from 'nfvo-components/input/SelectInput.jsx'; +import Overlay from 'react-bootstrap/lib/Overlay.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +export const other = {OTHER: 'Other'}; + +class InputOptions extends React.Component { + + static propTypes = { + values: React.PropTypes.arrayOf(React.PropTypes.shape({ + enum: React.PropTypes.string, + title: React.PropTypes.string + })), + isEnabledOther: React.PropTypes.bool, + label: React.PropTypes.string, + selectedValue: React.PropTypes.string, + multiSelectedEnum: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.array + ]), + selectedEnum: React.PropTypes.string, + otherValue: React.PropTypes.string, + overlayPos: React.PropTypes.string, + onEnumChange: React.PropTypes.func, + onOtherChange: React.PropTypes.func, + onBlur: React.PropTypes.func, + isRequired: React.PropTypes.bool, + isMultiSelect: React.PropTypes.bool, + isValid: React.PropTypes.bool, + disabled: React.PropTypes.bool + }; + + state = { + otherInputDisabled: !this.props.otherValue + }; + + oldProps = { + selectedEnum: '', + otherValue: '', + multiSelectedEnum: [] + }; + + render() { + let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, isValid, children, isReadOnlyMode} = this.props; + const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; + let currentMultiSelectedEnum = []; + let currentSelectedEnum = ''; + let {otherInputDisabled} = this.state; + if (isMultiSelect) { + currentMultiSelectedEnum = multiSelectedEnum; + if(!otherInputDisabled) { + currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined; + } + } + else if(selectedEnum){ + currentSelectedEnum = selectedEnum; + } + if (!onBlur) { + onBlur = () => {}; + } + + return( + <div className='validation-input-wrapper' > + <div className={classNames('form-group', {'required' : isRequired, 'has-error' : !isValid})} > + {label && <label className='control-label'>{label}</label>} + {isMultiSelect && otherInputDisabled ? + <Select + {...dataTestId} + ref={(input) => this.input = input} + value={currentMultiSelectedEnum} + className='options-input' + clearable={false} + required={isRequired} + disabled={isReadOnlyMode || Boolean(this.props.disabled)} + onBlur={() => onBlur()} + onMultiSelectChanged={value => this.multiSelectEnumChanged(value)} + options={this.renderMultiSelectOptions(values)} + multi/> : + <div className={classNames('input-options',{'has-error' : !isValid})} > + <select + {...dataTestId} + ref={(input) => this.input = input} + label={label} + className='form-control input-options-select' + value={currentSelectedEnum} + style={{'width' : otherInputDisabled ? '100%' : '100px'}} + onBlur={() => onBlur()} + disabled={isReadOnlyMode || Boolean(this.props.disabled)} + onChange={ value => this.enumChanged(value)} + type='select'> + {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))} + {onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>} + </select> + + {!otherInputDisabled && <div className='input-options-separator'/>} + <input + className='form-control input-options-other' + placeholder={i18n('other')} + ref={(otherValue) => this.otherValue = otherValue} + style={{'display' : otherInputDisabled ? 'none' : 'block'}} + disabled={isReadOnlyMode || Boolean(this.props.disabled)} + value={otherValue || ''} + onBlur={() => onBlur()} + onChange={() => this.changedOtherInput()}/> + </div> + } + </div> + { this.renderErrorOverlay() } + </div> + ); + } + + renderOptions(val, index){ + return ( + <option key={index} value={val.enum}>{val.title}</option> + ); + } + + + renderMultiSelectOptions(values) { + let {onOtherChange} = this.props; + let optionsList = []; + if (onOtherChange) { + optionsList = values.map(option => { + return { + label: option.title, + value: option.enum, + }; + }).concat([{ + label: i18n(other.OTHER), + value: i18n(other.OTHER), + }]); + } + else { + optionsList = values.map(option => { + return { + label: option.title, + value: option.enum, + }; + }); + } + if (optionsList.length > 0 && optionsList[0].value === '') { + optionsList.shift(); + } + return optionsList; + } + + renderErrorOverlay() { + let position = 'right'; + const {errorText = '', isValid = true, type, overlayPos} = this.props; + + if (overlayPos) { + position = overlayPos; + } + else if (type === 'text' + || type === 'email' + || type === 'number' + || type === 'password') { + position = 'bottom'; + } + + return ( + <Overlay + show={!isValid} + placement={position} + target={() => { + let {otherInputDisabled} = this.state; + let target = otherInputDisabled ? ReactDOM.findDOMNode(this.input) : ReactDOM.findDOMNode(this.otherValue); + return target.offsetParent ? target : undefined; + }} + container={this}> + <Tooltip + id={`error-${errorText.replace(' ', '-')}`} + className='validation-error-message'> + {errorText} + </Tooltip> + </Overlay> + ); + } + + getValue() { + let res = ''; + let {isMultiSelect} = this.props; + let {otherInputDisabled} = this.state; + + if (otherInputDisabled) { + res = isMultiSelect ? this.input.getValue() : this.input.value; + } else { + res = this.otherValue.value; + } + return res; + } + + enumChanged() { + let enumValue = this.input.value; + let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props; + this.setState({ + otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER + }); + + let value = isMultiSelect ? [enumValue] : enumValue; + if (onEnumChange) { + onEnumChange(value); + } + if (onChange) { + onChange(value); + } + } + + multiSelectEnumChanged(enumValue) { + let {onEnumChange, onOtherChange} = this.props; + let selectedValues = enumValue.map(enumVal => { + return enumVal.value; + }); + + if (this.state.otherInputDisabled === false) { + selectedValues.shift(); + } + else if (selectedValues.includes(i18n(other.OTHER))) { + selectedValues = [i18n(other.OTHER)]; + } + + this.setState({ + otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER)) + }); + onEnumChange(selectedValues); + } + + changedOtherInput() { + let {onOtherChange} = this.props; + onOtherChange(this.otherValue.value); + } + + componentDidUpdate() { + let {otherValue, selectedEnum, onInputChange, multiSelectedEnum} = this.props; + if (this.oldProps.otherValue !== otherValue + || this.oldProps.selectedEnum !== selectedEnum + || this.oldProps.multiSelectedEnum !== multiSelectedEnum) { + this.oldProps = { + otherValue, + selectedEnum, + multiSelectedEnum + }; + onInputChange(); + } + } + + static getTitleByName(values, name) { + for (let key of Object.keys(values)) { + let option = values[key].find(option => option.enum === name); + if (option) { + return option.title; + } + } + return name; + } + +} + +export default InputOptions; diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx new file mode 100644 index 0000000000..5ca716cc20 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx @@ -0,0 +1,134 @@ +/*! + * 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 ReactDOM from 'react-dom'; +import classNames from 'classnames'; +import Checkbox from 'react-bootstrap/lib/Checkbox.js'; +import Radio from 'react-bootstrap/lib/Radio.js'; +import FormGroup from 'react-bootstrap/lib/FormGroup.js'; +import FormControl from 'react-bootstrap/lib/FormControl.js'; + +class InputWrapper extends React.Component { + + state = { + value: this.props.value, + checked: this.props.checked, + selectedValues: [] + } + + render() { + const {label, hasError, validations = {}, isReadOnlyMode, value, onBlur, onKeyDown, type, disabled, checked, name} = this.props; + const {groupClassName, ...inputProps} = this.props; + return( + <FormGroup className={classNames('form-group', [groupClassName], {'required' : validations.required , 'has-error' : hasError})} > + {(label && (type !== 'checkbox' && type !== 'radio')) && <label className='control-label'>{label}</label>} + {(type === 'text' || type === 'number') && + <FormControl + bsClass={'form-control input-options-other'} + onChange={(e) => this.onChange(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + onBlur={onBlur} + onKeyDown={onKeyDown} + value={value || ''} + ref={(input) => this.inputWrapper = input} + type={type} + data-test-id={this.props['data-test-id']}/>} + + {type === 'textarea' && + <FormControl + className='form-control input-options-other' + disabled={isReadOnlyMode || Boolean(disabled)} + value={value || ''} + onBlur={onBlur} + onKeyDown={onKeyDown} + componentClass={type} + onChange={(e) => this.onChange(e)} + data-test-id={this.props['data-test-id']}/>} + + {type === 'checkbox' && + <Checkbox + className={classNames({'required' : validations.required , 'has-error' : hasError})} + onChange={(e)=>this.onChangeCheckBox(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + checked={value} + data-test-id={this.props['data-test-id']}>{label}</Checkbox>} + + {type === 'radio' && + <Radio name={name} + checked={checked} + disabled={isReadOnlyMode || Boolean(disabled)} + value={value} + onChange={(e)=>this.onChangeRadio(e)} + data-test-id={this.props['data-test-id']}>{label}</Radio>} + {type === 'select' && + <FormControl onClick={ (e) => this.optionSelect(e) } + componentClass={type} + name={name} {...inputProps} + data-test-id={this.props['data-test-id']}/>} + + </FormGroup> + + ); + } + + getValue() { + return this.props.type !== 'select' ? this.state.value : this.state.selectedValues; + } + + getChecked() { + return this.state.checked; + } + + optionSelect(e) { + let selectedValues = []; + if (e.target.value) { + selectedValues.push(e.target.value); + } + this.setState({ + selectedValues + }); + } + + onChange(e) { + let {onChange} = this.props; + this.setState({ + value: e.target.value + }); + onChange(e.target.value); + } + + onChangeCheckBox(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(e.target.checked); + } + + onChangeRadio(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(this.state.value); + } + + focus() { + ReactDOM.findDOMNode(this.inputWrapper).focus(); + } + +} +export default InputWrapper; diff --git a/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx new file mode 100644 index 0000000000..95144b1468 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/Tabs.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 ReactDOM from 'react-dom'; +import {default as BTabs} from 'react-bootstrap/lib/Tabs.js'; +import Overlay from 'react-bootstrap/lib/Overlay.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export default +class Tabs extends React.Component { + + static propTypes = { + children: React.PropTypes.node + }; + + cloneTab(element) { + const {invalidTabs} = this.props; + return React.cloneElement( + element, + { + key: element.props.eventKey, + tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab' + } + ); + } + + showTabsError() { + const {invalidTabs} = this.props; + const showError = ((invalidTabs.length === 1 && invalidTabs[0] !== this.props.activeKey) || (invalidTabs.length > 1)); + return showError; + } + + render() { + // eslint-disable-next-line no-unused-vars + let {invalidTabs, ...tabProps} = this.props; + return ( + <div> + <BTabs {...tabProps} ref='tabsList' id='tabsList' > + {this.props.children.map(element => this.cloneTab(element))} + </BTabs> + <Overlay + animation={false} + show={this.showTabsError()} + placement='bottom' + containerPadding={50} + target={() => { + let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); + return target && target.offsetParent ? target : undefined; + } + } + container={() => { + let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); + return target && target.offsetParent ? target.offsetParent : this; + }}> + <Tooltip + id='error-some-tabs-contain-errors' + className='validation-error-message'> + {i18n('One or more tabs are invalid')} + </Tooltip> + </Overlay> + </div> + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx index a87c8d6f40..ebb1473c04 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ /** * Holds the buttons for save/reset for forms. * Used by the ValidationForm that changes the state of the buttons according to its own state. @@ -8,7 +23,7 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Button from 'react-bootstrap/lib/Button.js'; -import FontAwesome from 'react-fontawesome'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; class ValidationButtons extends React.Component { @@ -22,8 +37,8 @@ class ValidationButtons extends React.Component { }; render() { - var submitBtn = this.props.labledButtons ? i18n('Save') : <FontAwesome className='check' name='check'/>; - var closeBtn = this.props.labledButtons ? i18n('Cancel') : <FontAwesome className='close' name='close'/>; + var submitBtn = this.props.labledButtons ? i18n('Save') : <SVGIcon className='check' name='check'/>; + var closeBtn = this.props.labledButtons ? i18n('Cancel') : <SVGIcon className='close' name='close'/>; return ( <div className='validation-buttons'> {!this.props.isReadOnlyMode ? diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx deleted file mode 100644 index 098ccf1fd4..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx +++ /dev/null @@ -1,200 +0,0 @@ -/** - * ValidationForm should be used in order to have a form that handles it's internal validation state. - * All ValidationInputs inside the form are checked for validity and the styling and submit buttons - * are updated accordingly. - * - * The properties that ahould be given to the form: - * labledButtons - whether or not use icons only as the form default buttons or use buttons with labels - * onSubmit - function for click on the submit button - * onReset - function for click on the reset button - */ -import React from 'react'; -import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; -import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; -import ValidationButtons from './ValidationButtons.jsx'; - -class ValidationForm extends React.Component { - - static childContextTypes = { - validationParent: React.PropTypes.any, - isReadOnlyMode: React.PropTypes.bool, - validationSchema: React.PropTypes.instanceOf(JSONSchema), - validationData: React.PropTypes.object - }; - - static defaultProps = { - hasButtons : true, - onSubmit : null, - onReset : null, - labledButtons: true, - onValidChange : null, - isValid: true - }; - - static propTypes = { - isValid : React.PropTypes.bool, - isReadOnlyMode : React.PropTypes.bool, - hasButtons : React.PropTypes.bool, - onSubmit : React.PropTypes.func, - onReset : React.PropTypes.func, - labledButtons: React.PropTypes.bool, - onValidChange : React.PropTypes.func, - onValidityChanged: React.PropTypes.func, - schema: React.PropTypes.object, - data: React.PropTypes.object - }; - - state = { - isValid: this.props.isValid - }; - - constructor(props) { - super(props); - this.validationComponents = []; - } - - componentWillMount() { - let {schema, data} = this.props; - if (schema) { - this.processSchema(schema, data); - } - } - - componentWillReceiveProps(nextProps) { - let {schema, data} = this.props; - let {schema: nextSchema, data: nextData} = nextProps; - - if (schema !== nextSchema || data !== nextData) { - if (!schema || !nextSchema) { - throw new Error('ValidationForm: dynamically adding/removing schema is not supported'); - } - - if (schema !== nextSchema) { - this.processSchema(nextSchema, nextData); - } else { - this.setState({data: nextData}); - } - } - } - - processSchema(rawSchema, rawData) { - let schema = new JSONSchema(); - schema.setSchema(rawSchema); - let data = schema.processData(rawData); - this.setState({ - schema, - data - }); - } - - render() { - // eslint-disable-next-line no-unused-vars - let {isValid, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, schema, data, children, ...formProps} = this.props; - return ( - <form {...formProps} onSubmit={event => this.handleFormSubmit(event)}> - <div className='validation-form-content'>{children}</div> - {hasButtons && <ValidationButtons labledButtons={labledButtons} ref='buttons' isReadOnlyMode={isReadOnlyMode}/>} - </form> - ); - } - - handleFormSubmit(event) { - event.preventDefault(); - let isFormValid = true; - this.validationComponents.forEach(validationComponent => { - const isInputValid = validationComponent.validate().isValid; - isFormValid = isInputValid && isFormValid; - }); - if(isFormValid && this.props.onSubmit) { - return this.props.onSubmit(event); - } else if(!isFormValid) { - this.setState({isValid: false}); - } - } - - componentWillUpdate(nextProps, nextState) { - if(this.state.isValid !== nextState.isValid && this.props.onValidityChanged) { - this.props.onValidityChanged(nextState.isValid); - } - } - - componentDidUpdate(prevProps, prevState) { - // only handling this programatically if the validation of the form is done outside of the view - // (example with a form that is dependent on the state of other forms) - if (prevProps.isValid !== this.props.isValid) { - if (this.props.hasButtons) { - this.refs.buttons.setState({isValid: this.state.isValid}); - } - } else if(this.state.isValid !== prevState.isValid) { - if (this.props.hasButtons) { - this.refs.buttons.setState({isValid: this.state.isValid}); - } - // callback in case form is part of bigger picture in view - if (this.props.onValidChange) { - this.props.onValidChange(this.state.isValid); - } - } - } - - componentDidMount() { - if (this.props.hasButtons) { - this.refs.buttons.setState({isValid: this.state.isValid}); - } - } - - - getChildContext() { - return { - validationParent: this, - isReadOnlyMode: this.props.isReadOnlyMode, - validationSchema: this.state.schema, - validationData: this.state.data - }; - } - - - /*** - * Used by ValidationInput in order to let the (parent) form know - * the valid state. If there is a change in the state of the form, - * the buttons will be updated. - * - * @param validationComponent - * @param isValid - */ - childValidStateChanged(validationComponent, isValid) { - if (isValid !== this.state.isValid) { - let oldState = this.state.isValid; - let newState = isValid && this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent).every(otherValidationComponent => { - return otherValidationComponent.isValid(); - }); - - if (oldState !== newState) { - this.setState({isValid: newState}); - } - } - } - - register(validationComponent) { - if (this.state.schema) { - // TODO: register - } else { - this.validationComponents.push(validationComponent); - } - } - - unregister(validationComponent) { - this.childValidStateChanged(validationComponent, true); - this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); - } - - onValueChanged(pointer, value, isValid, error) { - this.props.onDataChanged({ - data: JSONPointer.setValue(this.props.data, pointer, value), - isValid, - error - }); - } -} - - -export default ValidationForm; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx deleted file mode 100644 index 0f14307645..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx +++ /dev/null @@ -1,509 +0,0 @@ -/** - * Used for inputs on a validation form. - * All properties will be passed on to the input element. - * - * The following properties can be set for OOB validations and callbacks: - - required: Boolean: Should be set to true if the input must have a value - - numeric: Boolean : Should be set to true id the input should be an integer - - onChange : Function : Will be called to validate the value if the default validations are not sufficient, should return a boolean value - indicating whether the value is valid - - didUpdateCallback :Function: Will be called after the state has been updated and the component has rerendered. This can be used if - there are dependencies between inputs in a form. - * - * The following properties of the state can be set to determine - * the state of the input from outside components: - - isValid : Boolean - whether the value is valid - - value : value for the input field, - - disabled : Boolean, - - required : Boolean - whether the input value must be filled out. - */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Validator from 'validator'; -import FormGroup from 'react-bootstrap/lib/FormGroup.js'; -import Input from 'react-bootstrap/lib/Input.js'; -import Overlay from 'react-bootstrap/lib/Overlay.js'; -import Tooltip from 'react-bootstrap/lib/Tooltip.js'; -import isEqual from 'lodash/isEqual.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; -import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; - - -import InputOptions from '../inputOptions/InputOptions.jsx'; - -const globalValidationFunctions = { - required: value => value !== '', - maxLength: (value, length) => Validator.isLength(value, {max: length}), - minLength: (value, length) => Validator.isLength(value, {min: length}), - pattern: (value, pattern) => Validator.matches(value, pattern), - numeric: value => { - if (value === '') { - // to allow empty value which is not zero - return true; - } - return Validator.isNumeric(value); - }, - maxValue: (value, maxValue) => value < maxValue, - minValue: (value, minValue) => value >= minValue, - alphanumeric: value => Validator.isAlphanumeric(value), - alphanumericWithSpaces: value => Validator.isAlphanumeric(value.replace(/ /g, '')), - validateName: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-/g, ''), 'en-US'), - validateVendorName: value => Validator.isAlphanumeric(value.replace(/[\x7F-\xFF]|\s/g, ''), 'en-US'), - freeEnglishText: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-|\,|\(|\)|\?/g, ''), 'en-US'), - email: value => Validator.isEmail(value), - ip: value => Validator.isIP(value), - url: value => Validator.isURL(value) -}; - -const globalValidationMessagingFunctions = { - required: () => i18n('Field is required'), - 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}), - numeric: () => i18n('Field value should contain numbers only.'), - maxValue: (value, maxValue) => i18n('Field value should be less than: {maxValue}.', {maxValue}), - minValue: (value, minValue) => i18n('Field value should be at least: {minValue}.', {minValue}), - 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.'), - validateVendorName: ()=> i18n('Field value should contain English letters digits and spaces only.'), - freeEnglishText: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), - 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.') -}; - -class ValidationInput extends React.Component { - - static contextTypes = { - validationParent: React.PropTypes.any, - isReadOnlyMode: React.PropTypes.bool, - validationSchema: React.PropTypes.instanceOf(JSONSchema), - validationData: React.PropTypes.object - }; - - static defaultProps = { - onChange: null, - disabled: null, - didUpdateCallback: null, - validations: {}, - value: '' - }; - - static propTypes = { - type: React.PropTypes.string.isRequired, - onChange: React.PropTypes.func, - disabled: React.PropTypes.bool, - didUpdateCallback: React.PropTypes.func, - validations: React.PropTypes.object, - isMultiSelect: React.PropTypes.bool, - onOtherChange: React.PropTypes.func, - pointer: React.PropTypes.string - }; - - - state = { - isValid: true, - style: null, - value: this.props.value, - error: {}, - previousErrorMessage: '', - wasInvalid: false, - validations: this.props.validations, - isMultiSelect: this.props.isMultiSelect - }; - - componentWillMount() { - if (this.context.validationSchema) { - let {validationSchema: schema, validationData: data} = this.context, - {pointer} = this.props; - - if (!schema.exists(pointer)) { - console.error(`Field doesn't exists in the schema ${pointer}`); - } - - let value = JSONPointer.getValue(data, pointer); - if (value === undefined) { - value = schema.getDefault(pointer); - if (value === undefined) { - value = ''; - } - } - this.setState({value}); - - let enums = schema.getEnum(pointer); - if (enums) { - let values = enums.map(value => ({enum: value, title: value, groupName: pointer})), - isMultiSelect = schema.isArray(pointer); - - if (!isMultiSelect && this.props.type !== 'radiogroup') { - values = [{enum: '', title: i18n('Select...')}, ...values]; - } - if (isMultiSelect && Array.isArray(value) && value.length === 0) { - value = ''; - } - - this.setState({ - isMultiSelect, - values, - onEnumChange: value => this.changedInputOptions(value), - value - }); - } - - this.setState({validations: this.extractValidationsFromSchema(schema, pointer, this.props)}); - } - } - - extractValidationsFromSchema(schema, pointer, props) { - /* props are here to get precedence over the scheme definitions */ - let validations = {}; - - if (schema.isRequired(pointer)) { - validations.required = true; - } - - if (schema.isNumber(pointer)) { - validations.numeric = true; - - const maxValue = props.validations.maxValue || schema.getMaxValue(pointer); - if (maxValue !== undefined) { - validations.maxValue = maxValue; - } - - const minValue = props.validations.minValue || schema.getMinValue(pointer); - if (minValue !== undefined) { - validations.minValue = minValue; - } - } - - - if (schema.isString(pointer)) { - - const pattern = schema.getPattern(pointer); - if (pattern) { - validations.pattern = pattern; - } - - const maxLength = schema.getMaxLength(pointer); - if (maxLength !== undefined) { - validations.maxLength = maxLength; - } - - const minLength = schema.getMinLength(pointer); - if (minLength !== undefined) { - validations.minLength = minLength; - } - } - - return validations; - } - - componentWillReceiveProps({value: nextValue, validations: nextValidations, pointer: nextPointer}, nextContext) { - const {validations, value} = this.props; - const validationsChanged = !isEqual(validations, nextValidations); - if (nextContext.validationSchema) { - if (this.props.pointer !== nextPointer || - this.context.validationData !== nextContext.validationData) { - let currentValue = JSONPointer.getValue(this.context.validationData, this.props.pointer), - nextValue = JSONPointer.getValue(nextContext.validationData, nextPointer); - if(nextValue === undefined) { - nextValue = ''; - } - if (this.state.isMultiSelect && Array.isArray(nextValue) && nextValue.length === 0) { - nextValue = ''; - } - if (currentValue !== nextValue) { - this.setState({value: nextValue}); - } - if (validationsChanged) { - this.setState({ - validations: this.extractValidationsFromSchema(nextContext.validationSchema, nextPointer, {validations: nextValidations}) - }); - } - } - } else { - if (validationsChanged) { - this.setState({validations: nextValidations}); - } - if (this.state.wasInvalid && (value !== nextValue || validationsChanged)) { - this.validate(nextValue, nextValidations); - } else if (value !== nextValue) { - this.setState({value: nextValue}); - } - } - } - - shouldTypeBeNumberBySchemeDefinition(pointer) { - return this.context.validationSchema && - this.context.validationSchema.isNumber(pointer); - } - - hasEnum(pointer) { - return this.context.validationSchema && - this.context.validationSchema.getEnum(pointer); - } - - render() { - let {value, isMultiSelect, values, onEnumChange, style, isValid, validations} = this.state; - let {onOtherChange, type, pointer} = this.props; - if (this.shouldTypeBeNumberBySchemeDefinition(pointer) && !this.hasEnum(pointer)) { - type = 'number'; - } - let props = {...this.props}; - - let groupClasses = this.props.groupClassName || ''; - if (validations.required) { - groupClasses += ' required'; - } - let isReadOnlyMode = this.context.isReadOnlyMode; - - if (value === true && (type === 'checkbox' || type === 'radio')) { - props.checked = true; - } - return ( - <div className='validation-input-wrapper'> - { - !isMultiSelect && !onOtherChange && type !== 'select' && type !== 'radiogroup' - && <Input - {...props} - type={type} - groupClassName={groupClasses} - ref={'_myInput'} - value={value} - disabled={isReadOnlyMode || Boolean(this.props.disabled)} - bsStyle={style} - onChange={() => this.changedInput()} - onBlur={() => this.blurInput()}> - {this.props.children} - </Input> - } - { - type === 'radiogroup' - && <FormGroup> - { - values.map(val => - <Input disabled={isReadOnlyMode || Boolean(this.props.disabled)} - inline={true} - ref={'_myInput' + (typeof val.enum === 'string' ? val.enum.replace(/\W/g, '_') : val.enum)} - value={val.enum} checked={value === val.enum} - type='radio' label={val.title} - name={val.groupName} - onChange={() => this.changedInput()}/> - ) - } - </FormGroup> - } - { - (isMultiSelect || onOtherChange || type === 'select') - && <InputOptions - onInputChange={() => this.changedInput()} - onBlur={() => this.blurInput()} - hasError={!isValid} - ref={'_myInput'} - isMultiSelect={isMultiSelect} - values={values} - onEnumChange={onEnumChange} - selectedEnum={value} - multiSelectedEnum={value} - {...props} /> - } - {this.renderOverlay()} - </div> - ); - } - - renderOverlay() { - let position = 'right'; - if (this.props.type === 'text' - || this.props.type === 'email' - || this.props.type === 'number' - || this.props.type === 'password' - - ) { - position = 'bottom'; - } - - let validationMessage = this.state.error.message || this.state.previousErrorMessage; - return ( - <Overlay - show={!this.state.isValid} - placement={position} - target={() => { - let target = ReactDOM.findDOMNode(this.refs._myInput); - return target.offsetParent ? target : undefined; - }} - container={this}> - <Tooltip - id={`error-${validationMessage.replace(' ', '-')}`} - className='validation-error-message'> - {validationMessage} - </Tooltip> - </Overlay> - ); - } - - componentDidMount() { - if (this.context.validationParent) { - this.context.validationParent.register(this); - } - } - - componentDidUpdate(prevProps, prevState) { - if (this.context.validationParent) { - if (prevState.isValid !== this.state.isValid) { - this.context.validationParent.childValidStateChanged(this, this.state.isValid); - } - } - if (this.props.didUpdateCallback) { - this.props.didUpdateCallback(); - } - - } - - componentWillUnmount() { - if (this.context.validationParent) { - this.context.validationParent.unregister(this); - } - } - - isNumberInputElement() { - return this.props.type === 'number' || this.refs._myInput.props.type === 'number'; - } - - /*** - * Adding same method as the actual input component - * @returns {*} - */ - getValue() { - if (this.props.type === 'checkbox') { - return this.refs._myInput.getChecked(); - } - if (this.props.type === 'radiogroup') { - for (let key in this.refs) { // finding the value of the radio button that was checked - if (this.refs[key].getChecked()) { - return this.refs[key].getValue(); - } - } - } - if (this.isNumberInputElement()) { - return Number(this.refs._myInput.getValue()); - } - - return this.refs._myInput.getValue(); - } - - resetValue() { - this.setState({value: this.props.value}); - } - - - /*** - * internal method that validated the value. includes callback to the onChange method - * @param value - * @param validations - map containing validation id and the limitation describing the validation. - * @returns {object} - */ - validateValue = (value, validations) => { - let {customValidationFunction} = validations; - let error = {}; - let isValid = true; - for (let validation in validations) { - if ('customValidationFunction' !== validation) { - if (validations[validation]) { - if (!globalValidationFunctions[validation](value, validations[validation])) { - error.id = validation; - error.message = globalValidationMessagingFunctions[validation](value, validations[validation]); - isValid = false; - break; - } - } - } else { - let customValidationResult = customValidationFunction(value); - - if (customValidationResult !== true) { - error.id = 'custom'; - isValid = false; - if (typeof customValidationResult === 'string') {//custom validation error message supplied. - error.message = customValidationResult; - } else { - error.message = globalValidationMessagingFunctions.general(); - } - break; - } - - - } - } - - return { - isValid, - error - }; - }; - - /*** - * Internal method that handles the change event of the input. validates and updates the state. - */ - changedInput() { - - let {isValid, error} = this.state.wasInvalid ? this.validate() : this.state; - let onChange = this.props.onChange; - if (onChange) { - onChange(this.getValue(), isValid, error); - } - if (this.context.validationSchema) { - let value = this.getValue(); - if (this.state.isMultiSelect && value === '') { - value = []; - } - if (this.shouldTypeBeNumberBySchemeDefinition(this.props.pointer)) { - value = Number(value); - } - this.context.validationParent.onValueChanged(this.props.pointer, value, isValid, error); - } - } - - changedInputOptions(value) { - this.context.validationParent.onValueChanged(this.props.pointer, value, true); - } - - blurInput() { - if (!this.state.wasInvalid) { - this.setState({wasInvalid: true}); - } - - let {isValid, error} = !this.state.wasInvalid ? this.validate() : this.state; - let onBlur = this.props.onBlur; - if (onBlur) { - onBlur(this.getValue(), isValid, error); - } - } - - validate(value = this.getValue(), validations = this.state.validations) { - let validationStatus = this.validateValue(value, validations); - let {isValid, error} = validationStatus; - let _style = isValid ? null : 'error'; - this.setState({ - isValid, - error, - value, - previousErrorMessage: this.state.error.message || '', - style: _style, - wasInvalid: !isValid || this.state.wasInvalid - }); - - return validationStatus; - } - - isValid() { - return this.state.isValid; - } - -} -export default ValidationInput; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx deleted file mode 100644 index 6036518288..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react'; -import Tab from 'react-bootstrap/lib/Tab.js'; - -export default -class ValidationTab extends React.Component { - - static propTypes = { - children: React.PropTypes.node, - eventKey: React.PropTypes.any.isRequired, - onValidationStateChange: React.PropTypes.func //This property is assigned dynamically via React.cloneElement. lookup ValidationTabs.jsx. therefore it cannot be stated as required! - }; - - constructor(props) { - super(props); - this.validationComponents = []; - } - - static childContextTypes = { - validationParent: React.PropTypes.any - }; - - static contextTypes = { - validationParent: React.PropTypes.any - }; - - getChildContext() { - return {validationParent: this}; - } - - state = { - isValid: true, - notifyParent: false - }; - - componentDidMount() { - let validationParent = this.context.validationParent; - if (validationParent) { - validationParent.register(this); - } - } - - componentWillUnmount() { - let validationParent = this.context.validationParent; - if (validationParent) { - validationParent.unregister(this); - } - } - - register(validationComponent) { - this.validationComponents.push(validationComponent); - } - - unregister(validationComponent) { - this.childValidStateChanged(validationComponent, true); - this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); - } - - notifyValidStateChangedToParent(isValid) { - - let validationParent = this.context.validationParent; - if (validationParent) { - validationParent.childValidStateChanged(this, isValid); - } - } - - childValidStateChanged(validationComponent, isValid) { - - const currentValidState = this.state.isValid; - if (isValid !== currentValidState) { - let filteredValidationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); - let newValidState = isValid && filteredValidationComponents.every(otherValidationComponent => { - return otherValidationComponent.isValid(); - }); - this.setState({isValid: newValidState, notifyParent: true}); - } - } - - validate() { - let isValid = true; - this.validationComponents.forEach(validationComponent => { - const isValidationComponentValid = validationComponent.validate().isValid; - isValid = isValidationComponentValid && isValid; - }); - this.setState({isValid, notifyParent: false}); - return {isValid}; - } - - componentDidUpdate(prevProps, prevState) { - if(prevState.isValid !== this.state.isValid) { - if(this.state.notifyParent) { - this.notifyValidStateChangedToParent(this.state.isValid); - } - this.props.onValidationStateChange(this.props.eventKey, this.state.isValid); - } - } - - isValid() { - return this.state.isValid; - } - - render() { - let {children, ...tabProps} = this.props; - return ( - <Tab {...tabProps}>{children}</Tab> - ); - } -} diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx deleted file mode 100644 index 6eda4b9827..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Tabs from 'react-bootstrap/lib/Tabs.js'; -import Overlay from 'react-bootstrap/lib/Overlay.js'; -import Tooltip from 'react-bootstrap/lib/Tooltip.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; - -export default -class ValidationTab extends React.Component { - - static propTypes = { - children: React.PropTypes.node - }; - - state = { - invalidTabs: [] - }; - - cloneTab(element) { - const {invalidTabs} = this.state; - return React.cloneElement( - element, - { - key: element.props.eventKey, - tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab', - onValidationStateChange: (eventKey, isValid) => this.validTabStateChanged(eventKey, isValid) - } - ); - } - - validTabStateChanged(eventKey, isValid) { - let {invalidTabs} = this.state; - let invalidTabIndex = invalidTabs.indexOf(eventKey); - if (isValid && invalidTabIndex > -1) { - this.setState({invalidTabs: invalidTabs.filter(otherEventKey => eventKey !== otherEventKey)}); - } else if (!isValid && invalidTabIndex === -1) { - this.setState({invalidTabs: [...invalidTabs, eventKey]}); - } - } - - showTabsError() { - const {invalidTabs} = this.state; - return invalidTabs.length > 0 && (invalidTabs.length > 1 || invalidTabs[0] !== this.props.activeKey); - } - - render() { - return ( - <div> - <Tabs {...this.props} ref='tabsList'> - {this.props.children.map(element => this.cloneTab(element))} - </Tabs> - <Overlay - animation={false} - show={this.showTabsError()} - placement='bottom' - target={() => { - let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); - return target && target.offsetParent ? target : undefined; - } - } - container={this}> - <Tooltip - id='error-some-tabs-contain-errors' - className='validation-error-message'> - {i18n('One or more tabs are invalid')} - </Tooltip> - </Overlay> - </div> - ); - } -} diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx index e8d0fc2536..f6c906b56b 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx @@ -1,7 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; +import classnames from 'classnames'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; import store from 'sdc-app/AppStore.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; class ListEditorItem extends React.Component { static propTypes = { @@ -16,14 +33,14 @@ class ListEditorItem extends React.Component { let {onDelete, onSelect, onEdit, children, isReadOnlyMode} = this.props; let isAbilityToDelete = isReadOnlyMode === undefined ? true : !isReadOnlyMode; return ( - <div className='list-editor-item-view'> + <div className={classnames('list-editor-item-view', {'selectable': Boolean(onSelect)})} data-test-id='list-editor-item'> <div className='list-editor-item-view-content' onClick={onSelect}> {children} </div> - <div className='list-editor-item-view-controller'> - {onEdit && <FontAwesome name='sliders' onClick={() => this.onClickedItem(onEdit)}/>} - {onDelete && isAbilityToDelete && <FontAwesome name='trash-o' onClick={() => this.onClickedItem(onDelete)}/>} - </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)}/>} + </div>} </div> ); } @@ -33,8 +50,11 @@ class ListEditorItem extends React.Component { let {isCheckedOut} = this.props; if (isCheckedOut === false) { store.dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {title: 'Error', msg: 'This item is checkedin/submitted, Click Check Out to continue'} + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data: { + title: i18n('Error'), + msg: i18n('This item is checkedin/submitted, Click Check Out to continue') + } }); } else { diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx new file mode 100644 index 0000000000..839f9a504a --- /dev/null +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx @@ -0,0 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; + +export const ListEditorItemViewField = ({children}) => ( + <div className='list-editor-item-view-field'> + {children} + </div> +); + +export default ListEditorItemViewField; diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx index 1ee91f31f6..cc805e9ada 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx @@ -1,12 +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 React from 'react'; -import FontAwesome from 'react-fontawesome'; -import Input from 'react-bootstrap/lib/Input.js'; +import classnames from 'classnames'; +import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; +const ListEditorHeader = ({onAdd, isReadOnlyMode, title, plusButtonTitle}) => { + return ( + <div className='list-editor-view-header'> + {title && <div className='list-editor-view-title'>{title}</div>} + <div className={`list-editor-view-add-controller${isReadOnlyMode ? ' disabled' : ''}`}> + { onAdd && + <div className='list-editor-view-add-title' data-test-id='add-button' onClick={onAdd}> + <span>{`+ ${plusButtonTitle}`}</span> + </div> + } + </div> + </div> + ); +}; + +const ListEditorScroller = ({children, twoColumns}) => { + return ( + <div className='list-editor-view-list-scroller'> + <div className={classnames('list-editor-view-list', {'two-columns': twoColumns})}> + {children} + </div> + </div> + ); +}; + +const FilterWrapper = ({onFilter, filterValue}) => { + return ( + <div className='expandble-search-wrapper'> + <ExpandableInput + onChange={onFilter} + iconType='search' + value={filterValue}/> + </div> + ); +}; class ListEditorView extends React.Component { static defaultProps = { - className: '' + className: '', + twoColumns: false }; static propTypes = { @@ -17,45 +68,17 @@ class ListEditorView extends React.Component { onFilter: React.PropTypes.func, className: React.PropTypes.string, isReadOnlyMode: React.PropTypes.bool, - placeholder: React.PropTypes.string + placeholder: React.PropTypes.string, + twoColumns: React.PropTypes.bool }; render() { - let {title, plusButtonTitle, onAdd, children, filterValue, onFilter, className, placeholder, isReadOnlyMode} = this.props; + let {title, plusButtonTitle, onAdd, children, onFilter, className, isReadOnlyMode, twoColumns, filterValue} = this.props; return ( - <div className={`list-editor-view ${className}`}> - {title && onAdd && <div className='list-editor-view-title'>{title}</div>} - <div className='list-editor-view-actions'> - {title && !onAdd && <div className='list-editor-view-title-inline'>{title}</div>} - <div className={`list-editor-view-add-controller${isReadOnlyMode ? ' disabled' : ''}`} > - { onAdd && - <div onClick={onAdd}> - <span className='plus-icon-button pull-left'/> - <span>{plusButtonTitle}</span> - </div> - } - </div> - - { - onFilter && - <div className='list-editor-view-search search-wrapper'> - <Input - ref='filter' - type='text' - value={filterValue} - name='list-editor-view-search' - placeholder={placeholder} - groupClassName='search-input-control' - onChange={() => onFilter(this.refs.filter.getValue())}/> - <FontAwesome name='filter' className='filter-icon'/> - </div> - } - </div> - <div className='list-editor-view-list-scroller'> - <div className='list-editor-view-list'> - {children} - </div> - </div> + <div className={classnames('list-editor-view', className)}> + <ListEditorHeader onAdd={onAdd} isReadOnlyMode={isReadOnlyMode} plusButtonTitle={plusButtonTitle} title={title}/> + {onFilter && (children.length || filterValue) && <FilterWrapper onFilter={onFilter} filterValue={filterValue}/>} + <ListEditorScroller children={children} twoColumns={twoColumns}/> </div> ); } diff --git a/openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js b/openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js new file mode 100644 index 0000000000..276b05e270 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js @@ -0,0 +1,60 @@ +import React from 'react'; +import {storiesOf, action} from '@kadira/storybook'; +import ListEditorView from './ListEditorView.jsx'; +import ListEditorItemView from './ListEditorItemView.jsx'; +import ListEditorItemViewField from './ListEditorItemViewField.jsx'; +import {text, number} from '@kadira/storybook-addon-knobs'; +import {withKnobs} from '@kadira/storybook-addon-knobs'; + +function makeChildren({onEdit = false, onDelete = false} = {}) { + return ( + [...Array(number('Items', 2)).keys()].map(index => ( + <ListEditorItemView + key={index} + onEdit={onEdit ? onEdit : undefined} + onDelete={onDelete ? onDelete : undefined}> + <ListEditorItemViewField> + <div>{text('field 1', 'Lorum Ipsum')}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div>{text('field 2', 'Lorum Ipsum')}</div> + </ListEditorItemViewField> + </ListEditorItemView>) + ) + ); +} + +const stories = storiesOf('ListEditor', module); +stories.addDecorator(withKnobs); + +stories + .add('regular', () => ( + <ListEditorView title='List Editor'> + {makeChildren()} + </ListEditorView> + )) + .add('two columns', () => ( + <ListEditorView title='List Editor' twoColumns> + {makeChildren()} + </ListEditorView> + )) + .add('with add', () => ( + <ListEditorView title='List Editor' onAdd={action('onAdd')} plusButtonTitle='Add' twoColumns> + {makeChildren()} + </ListEditorView> + )) + .add('with delete', () => ( + <ListEditorView title='List Editor' onAdd={action('onAdd')} plusButtonTitle='Add' twoColumns> + {makeChildren({onDelete: action('onDelete')})} + </ListEditorView> + )) + .add('with edit', () => ( + <ListEditorView title='List Editor' onAdd={action('onAdd')} plusButtonTitle='Add' twoColumns> + {makeChildren({onEdit: action('onEdit')})} + </ListEditorView> + )) + .add('with edit and delete', () => ( + <ListEditorView title='List Editor' onAdd={action('onAdd')} plusButtonTitle='Add' twoColumns> + {makeChildren({onDelete: action('onDelete'), onEdit: action('onEdit')})} + </ListEditorView> + )); diff --git a/openecomp-ui/src/nfvo-components/loader/Loader.jsx b/openecomp-ui/src/nfvo-components/loader/Loader.jsx index cc1ffdb2b3..675b04c8ea 100644 --- a/openecomp-ui/src/nfvo-components/loader/Loader.jsx +++ b/openecomp-ui/src/nfvo-components/loader/Loader.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import {connect} from 'react-redux'; diff --git a/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js b/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js index e8e4953eb9..7c0c0e2b08 100644 --- a/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js +++ b/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ diff --git a/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js b/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js index 582eff330d..2eff70a617 100644 --- a/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js +++ b/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './LoaderConstants.js'; export default (state = {}, action) => { diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModal.js b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js new file mode 100644 index 0000000000..65a1ad683b --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js @@ -0,0 +1,120 @@ +/*! + * 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 Modal from 'nfvo-components/modal/Modal.jsx'; +import Button from 'react-bootstrap/lib/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', + warning: 'warning', + success: 'success' +}; + + +const ModalFooter = ({type, onConfirmed, onDeclined, onClose, confirmationButtonText, cancelButtonText}) => + <Modal.Footer> + <Button bsStyle={typeClass[type]} onClick={onDeclined ? () => { + onDeclined(); + onClose();} : () => onClose()}> + {cancelButtonText} + </Button> + {onConfirmed && <Button bsStyle={typeClass[type]} onClick={() => { + onConfirmed(); + onClose(); + }}>{confirmationButtonText}</Button>} + </Modal.Footer>; + +ModalFooter.defaultProps = { + type: 'default', + confirmationButtonText: i18n('OK'), + cancelButtonText: i18n('Cancel') +}; + +export const mapStateToProps = ({modal}) => { + const show = !!modal; + return { + show, + ...modal + }; +}; + +export const mapActionToProps = (dispatch) => { + return { + onClose: () => dispatch({type: actionTypes.GLOBAL_MODAL_CLOSE}) + }; +}; + + +export class GlobalModalView extends React.Component { + + static propTypes = { + show: React.PropTypes.bool, + type: React.PropTypes.oneOf(['default', 'error', 'warning', 'success']), + title: React.PropTypes.string, + modalComponentProps: React.PropTypes.object, + modalComponentName: React.PropTypes.string, + onConfirmed: React.PropTypes.func, + onDeclined: React.PropTypes.func, + confirmationButtonText: React.PropTypes.string, + cancelButtonText: React.PropTypes.string + }; + + static defaultProps = { + show: false, + type: 'default', + title: '' + }; + + render() { + let {title, type, show, modalComponentName, modalComponentProps, + modalClassName, msg, onConfirmed, onDeclined, confirmationButtonText, cancelButtonText, onClose} = this.props; + const ComponentToRender = modalContentComponents[modalComponentName]; + return ( + <Modal show={show} bsSize={modalComponentProps && modalComponentProps.size} className={`onborading-modal ${modalClassName || ''} ${typeClass[type]}`}> + <Modal.Header> + <Modal.Title>{title}</Modal.Title> + </Modal.Header> + <Modal.Body> + {ComponentToRender ? <ComponentToRender {...modalComponentProps}/> : msg} + </Modal.Body> + {(onConfirmed || onDeclined || type !== typeEnum.DEFAULT) && + <ModalFooter + type={type} + onConfirmed={onConfirmed} + onDeclined={onDeclined} + onClose={onClose} + confirmationButtonText={confirmationButtonText} + cancelButtonText={cancelButtonText}/>} + </Modal> + ); + } + + componentDidUpdate() { + 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 new file mode 100644 index 0000000000..0a0ed1fd71 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.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 keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + GLOBAL_MODAL_SHOW: null, + GLOBAL_MODAL_CLOSE: null, + GLOBAL_MODAL_ERROR: null, + GLOBAL_MODAL_WARNING: null, + GLOBAL_MODAL_SUCCESS: null, + +}); + + +export const typeEnum = { + DEFAULT: 'default', + ERROR: 'error', + WARNING: 'warning', + SUCCESS: 'success' +}; diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.js b/openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.js new file mode 100644 index 0000000000..28674ea569 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.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 {actionTypes, typeEnum} from './GlobalModalConstants.js'; + +export default (state = null, action) => { + switch (action.type) { + case actionTypes.GLOBAL_MODAL_SHOW: + return { + ...action.data + }; + case actionTypes.GLOBAL_MODAL_ERROR: + return { + type: typeEnum.ERROR, + modalClassName: 'notification-modal', + ...action.data + }; + case actionTypes.GLOBAL_MODAL_WARNING: + return { + type: typeEnum.WARNING, + modalClassName: 'notification-modal', + ...action.data + }; + + case actionTypes.GLOBAL_MODAL_SUCCESS: + return { + type: typeEnum.SUCCESS, + modalClassName: 'notification-modal', + ...action.data + }; + + case actionTypes.GLOBAL_MODAL_CLOSE: + return null; + default: + return state; + } +}; diff --git a/openecomp-ui/src/nfvo-components/modal/Modal.jsx b/openecomp-ui/src/nfvo-components/modal/Modal.jsx index be4963ef65..b0f704dba9 100644 --- a/openecomp-ui/src/nfvo-components/modal/Modal.jsx +++ b/openecomp-ui/src/nfvo-components/modal/Modal.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import ReactDOM from 'react-dom'; import BootstrapModal from 'react-bootstrap/lib/Modal.js'; diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js b/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js deleted file mode 100644 index 1a53f4c135..0000000000 --- a/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js +++ /dev/null @@ -1,29 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import keyMirror from 'nfvo-utils/KeyMirror.js'; - -export default keyMirror({ - NOTIFY_ERROR: null, - NOTIFY_SUCCESS: null, - NOTIFY_WARNING: null, - NOTIFY_INFO: null, - NOTIFY_CLOSE: null -}); diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx b/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx deleted file mode 100644 index 71793097fb..0000000000 --- a/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx +++ /dev/null @@ -1,100 +0,0 @@ -/** - * NotificationModal options: - * - * show: whether to show notification or not, - * type: the type of the notification. valid values are: 'default', 'error', 'warning', 'success' - * msg: the notification content. could be a string or node (React component) - * title: the notification title - * timeout: timeout for the notification to fade out. if timeout == 0 then the notification is rendered until the user closes it - * - */ -import React, {Component, PropTypes} from 'react'; -import {connect} from 'react-redux'; -import Button from 'react-bootstrap/lib/Button.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Modal from 'nfvo-components/modal/Modal.jsx'; -import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx'; -import NotificationConstants from './NotificationConstants.js'; - -let typeClass = { - 'default': 'primary', - error: 'danger', - warning: 'warning', - success: 'success' -}; - -const mapActionsToProps = (dispatch) => { - return {onCloseClick: () => dispatch({type: NotificationConstants.NOTIFY_CLOSE})}; -}; - -const mapStateToProps = ({notification}) => { - - let show = notification !== null && notification.title !== 'Conflict'; - let mapResult = {show}; - if (show) { - mapResult = {show, ...notification}; - } - - return mapResult; -}; - -export class NotificationModal extends Component { - - static propTypes = { - show: PropTypes.bool, - type: PropTypes.oneOf(['default', 'error', 'warning', 'success']), - title: PropTypes.string, - msg: PropTypes.node, - validationResponse: PropTypes.object, - timeout: PropTypes.number - }; - - static defaultProps = { - show: false, - type: 'default', - title: '', - msg: '', - timeout: 0 - }; - - state = {type: undefined}; - - componentWillReceiveProps(nextProps) { - if (this.props.show !== nextProps.show && nextProps.show === false) { - this.setState({type: this.props.type}); - } - else { - this.setState({type: undefined}); - } - } - - componentDidUpdate() { - if (this.props.timeout) { - setTimeout(this.props.onCloseClick, this.props.timeout); - } - } - - render() { - let {title, type, msg, show, validationResponse, onCloseClick} = this.props; - if (!show) { - type = this.state.type; - } - if (validationResponse) { - msg = (<SubmitErrorResponse validationResponse={validationResponse}/>); - } - return ( - <Modal show={show} className={`notification-modal ${typeClass[type]}`}> - <Modal.Header> - <Modal.Title>{title}</Modal.Title> - </Modal.Header> - <Modal.Body>{msg}</Modal.Body> - <Modal.Footer> - <Button bsStyle={typeClass[type]} onClick={onCloseClick}>{i18n('OK')}</Button> - </Modal.Footer> - </Modal> - ); - } -} - -export default connect(mapStateToProps, mapActionsToProps)(NotificationModal); diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js b/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js deleted file mode 100644 index c8b30d6e50..0000000000 --- a/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import NotificationConstants from './NotificationConstants.js'; - -export default (state = null, action) => { - switch (action.type) { - case NotificationConstants.NOTIFY_INFO: - return {type: 'default', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout}; - - case NotificationConstants.NOTIFY_ERROR: - return { - type: 'error', - title: action.data.title, - msg: action.data.msg, - validationResponse: action.data.validationResponse, - timeout: action.data.timeout - }; - - case NotificationConstants.NOTIFY_WARNING: - return {type: 'warning', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout}; - - case NotificationConstants.NOTIFY_SUCCESS: - return { - type: 'success', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout - }; - case NotificationConstants.NOTIFY_CLOSE: - return null; - - default: - return state; - } - -}; diff --git a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx index feb0f813ea..3b89137090 100644 --- a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx +++ b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx @@ -1,9 +1,23 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import classnames from 'classnames'; import Collapse from 'react-bootstrap/lib/Collapse.js'; class NavigationSideBar extends React.Component { - static PropTypes = { activeItemId: React.PropTypes.string.isRequired, onSelect: React.PropTypes.func, @@ -11,51 +25,26 @@ class NavigationSideBar extends React.Component { groups: React.PropTypes.array }; + constructor(props) { + super(props); + this.state = { + activeItemId: null + }; + this.handleItemClicked = this.handleItemClicked.bind(this); + } + render() { let {groups, activeItemId} = this.props; return ( <div className='navigation-side-content'> {groups.map(group => ( - <div className='navigation-group' key={group.id}> - <div className='group-name'>{group.name}</div> - <div className='navigation-group-items'> - { - group.items && group.items.map(item => this.renderGroupItem(item, activeItemId)) - } - </div> - </div> + <NavigationMenu menu={group} activeItemId={activeItemId} onNavigationItemClick={this.handleItemClicked} key={'menu_' + group.id} /> ))} </div> ); } - renderGroupItem(item, activeItemId) { - let isGroup = item.items && item.items.length > 0; - return ( - <div className={classnames('navigation-group-item', {'selected-item': item.id === activeItemId})}> - <div - key={item.id} - className={classnames('navigation-group-item-name', { - 'selected': item.id === activeItemId, - 'disabled': item.disabled, - 'bold-name': item.expanded, - 'hidden': item.hidden - })} - onClick={(event) => this.handleItemClicked(event, item)}> - {item.name} - </div> - {isGroup && - <Collapse in={item.expanded}> - <div> - {item.items.map(item => this.renderGroupItem(item, activeItemId))} - </div> - </Collapse> - } - </div> - ); - } - handleItemClicked(event, item) { event.stopPropagation(); if(this.props.onToggle) { @@ -70,4 +59,70 @@ class NavigationSideBar extends React.Component { } } +class NavigationMenu extends React.Component { + static PropTypes = { + activeItemId: React.PropTypes.string.isRequired, + onNavigationItemClick: React.PropTypes.func, + menu: React.PropTypes.array + }; + + render() { + const {menu, activeItemId, onNavigationItemClick} = this.props; + return ( + <div className='navigation-group' key={menu.id}> + <NavigationMenuHeader title={menu.name} /> + <NavigationMenuItems items={menu.items} activeItemId={activeItemId} onNavigationItemClick={onNavigationItemClick} /> + </div>); + } +} + +function NavigationMenuHeader(props) { + return <div className='group-name' data-test-id='navbar-group-name'>{props.title}</div>; +} + +function NavigationMenuItems(props) { + const {items, activeItemId, onNavigationItemClick} = props; + return ( + <div className='navigation-group-items'> + { + items && items.map(item => (<NavigationMenuItem key={'menuItem_' + item.id} item={item} activeItemId={activeItemId} onNavigationItemClick={onNavigationItemClick} />)) + } + </div> + ); +} + +function NavigationMenuItem(props) { + const {onNavigationItemClick, item, activeItemId} = props; + const isGroup = item.items && item.items.length > 0; + return ( + <div className={classnames('navigation-group-item', {'selected-item': item.id === activeItemId})} key={'item_' + item.id}> + <NavigationLink item={item} activeItemId={activeItemId} onClick={onNavigationItemClick} /> + {isGroup && <Collapse in={item.expanded} data-test-id={'navigation-group-' + item.id}> + <div> + {item.items.map(subItem => (<NavigationMenuItem key={'menuItem_' + subItem.id} item={subItem} onNavigationItemClick={onNavigationItemClick} activeItemId={activeItemId} />)) } + </div> + </Collapse> + } + </div> + ); +} + +function NavigationLink(props) { + const {item, activeItemId, onClick} = props; + return ( + <div + key={'navAction_' + item.id} + className={classnames('navigation-group-item-name', { + 'selected': item.id === activeItemId, + 'disabled': item.disabled, + 'bold-name': item.expanded, + 'hidden': item.hidden + })} + onClick={(event) => onClick(event, item)} + data-test-id={'navbar-group-item-' + item.id}> + {item.name} + </div> + ); +} + export default NavigationSideBar; diff --git a/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx b/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx deleted file mode 100644 index 10c5326300..0000000000 --- a/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import ReactDOM from 'react-dom'; - -class SlidePanel extends React.Component { - - static PropTypes = { - direction: React.PropTypes.string.isRequired, - className: React.PropTypes.string, - title: React.PropTypes.string, - isOpen: React.PropTypes.bool - }; - - static defaultProps = { - title: '', - className: '', - isOpen: true - }; - - state = { - isOpen: this.props.isOpen, - direction: this.props.direction, - width: 0, - arrowWidth: 0 - }; - - componentDidMount() { - this.setSliderPosition(); - } - - componentDidUpdate() { - this.setSliderPosition(); - } - - render() { - - let {children, className} = this.props; - let {isOpen} = this.state; - - return ( - <div className={ `slide-panel ${className}`}> - {this.renderHeader(isOpen)} - <div className={'slide-panel-content ' + (isOpen ? 'opened' : 'closed')}>{children}</div> - </div> - ); - } - - renderHeader(isOpen) { - let {direction: initialDirection, title} = this.props; - let {direction: currentDirection} = this.state; - - let iconName = currentDirection === 'right' ? 'angle-double-right collapse-double-icon' : 'angle-double-left collapse-double-icon'; - - let awestyle = {padding: '5px'}; - - if (!isOpen && initialDirection === 'right') { - awestyle.marginLeft = '-1px'; - } - return ( - <div className='slide-panel-header'> - { initialDirection === 'left' && <span className='slide-panel-header-title'>{title}</span>} - <FontAwesome - ref='arrowIcon' - style={awestyle} - onClick={this.handleClick} - className='pull-right' - name={iconName} - size='2x'/> - { initialDirection === 'right' && <span className='slide-panel-header-title'>{title}</span>} - </div> - ); - } - - handleClick = () => { - this.setState({ - isOpen: !this.state.isOpen, - direction: this.state.direction === 'left' ? 'right' : 'left' - }); - } - - setSliderPosition = () => { - - let el = ReactDOM.findDOMNode(this); - let {style} = el; - - let {direction: initialDirection} = this.props; - let arrowIconSize = Math.floor(ReactDOM.findDOMNode(this.refs.arrowIcon).getBoundingClientRect().width) * 2; - if (!this.state.isOpen) { - if (this.props.direction === 'left') { - style.left = arrowIconSize - el.getBoundingClientRect().width + 'px'; - } - if (initialDirection === 'right') { - style.right = arrowIconSize - el.getBoundingClientRect().width + 'px'; - } - } - else { - if (initialDirection === 'left') { - style.left = '0px'; - } - - if (this.props.direction === 'right') { - style.right = '0px'; - } - } - } - -} - -export default SlidePanel;
\ No newline at end of file diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx index 78525f84c6..6d900dd0bb 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx @@ -1,17 +1,31 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import classnames from 'classnames'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import Navbar from 'react-bootstrap/lib/Navbar.js'; -import Nav from 'react-bootstrap/lib/Nav.js'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import {actionsEnum, statusEnum} from './VersionControllerConstants.js'; +import {actionsEnum, statusEnum, statusBarTextMap } from './VersionControllerConstants.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; class VersionController extends React.Component { static propTypes = { - version: React.PropTypes.string, + version: React.PropTypes.object, viewableVersions: React.PropTypes.array, onVersionSwitching: React.PropTypes.func, isCheckedOut: React.PropTypes.bool.isRequired, @@ -23,143 +37,146 @@ class VersionController extends React.Component { }; render() { - let {status, isCheckedOut, version = '', viewableVersions = [], onVersionSwitching, callVCAction, onSave, isFormDataValid, onClose} = this.props; + let {status, isCheckedOut, version = {}, viewableVersions = [], onVersionSwitching, callVCAction, onSave, isFormDataValid, onClose} = this.props; let isCheckedIn = Boolean(status === statusEnum.CHECK_IN_STATUS); - let isLatestVersion = Boolean(version === viewableVersions[viewableVersions.length - 1]); + let isLatestVersion = Boolean(version.id === viewableVersions[viewableVersions.length - 1].id); if (!isLatestVersion) { status = statusEnum.PREVIOUS_VERSION; } - return ( <div className='version-controller-bar'> - <Navbar inverse className='navbar'> - <Navbar.Collapse> - <Nav className='items-in-left'> - <div className='version-section'> - <ValidationInput - type='select' - selectedEnum={version} - onEnumChange={value => onVersionSwitching && onVersionSwitching(value)}> - {viewableVersions && viewableVersions.map(viewVersion => { - return ( - <option key={viewVersion} value={viewVersion}>{`V ${viewVersion}`}</option> - ); - }) - } - {!viewableVersions.includes(version) && - <option key={version} value={version}>{`V ${version}`}</option>} - </ValidationInput> - </div> - <div className='vc-status'> - <div className='onboarding-status-icon'></div> - <div className='status-text'> {i18n('ONBOARDING')} - <div className='status-text-dash'> -</div> - </div> - {this.renderStatus(status)} - </div> - </Nav> - <Nav pullRight> - <div className='items-in-right'> - <div className='action-buttons'> - {callVCAction && - <div className='version-control-buttons'> - <div - className={classnames('vc-nav-item-button button-submit', {'disabled': !isCheckedIn || !isLatestVersion})} - onClick={() => this.submit(callVCAction)}> - {i18n('Submit')} - </div> - <div - className={classnames('vc-nav-item-button button-checkin-checkout', {'disabled': status === statusEnum.LOCK_STATUS || !isLatestVersion})} - onClick={() => this.checkinCheckoutVersion(callVCAction)}> - {`${isCheckedOut ? i18n('Check In') : i18n('Check Out')}`} - </div> - <div - className={classnames('sprite-new revert-btn ng-scope ng-isolate-scope', {'disabled': !isCheckedOut || version === '0.1' || !isLatestVersion})} - onClick={() => this.revertCheckout(callVCAction)}> - </div> - </div> - } - {onSave && - <div - className={classnames('sprite-new save-btn ng-scope ng-isolate-scope', {'disabled': !isCheckedOut || !isFormDataValid || !isLatestVersion})} - onClick={() => onSave()}> - </div> - } - </div> - <div className='vc-nav-item-close' onClick={() => onClose && onClose()}> X</div> - </div> - </Nav> - </Navbar.Collapse> - </Navbar> + <div className='vc-container'> + <div className='version-status-container'> + <VersionSelector viewableVersions={viewableVersions} version={version} onVersionSwitching={onVersionSwitching} /> + <StatusBarUpdates status={status}/> + </div> + <div className='save-submit-cancel-container'> + <ActionButtons onSubmit={callVCAction ? () => this.submit(callVCAction, version) : undefined} + onRevert={callVCAction ? () => this.revertCheckout(callVCAction, version) : undefined} + status={status} + onCheckinCheckout={callVCAction ? () => this.checkinCheckoutVersion(callVCAction, version) : undefined} + onSave={onSave ? () => onSave() : undefined} + isLatestVersion={isLatestVersion} + isCheckedOut={isCheckedOut} + isCheckedIn={isCheckedIn} isFormDataValid={isFormDataValid} version={version}/> + {onClose && <div className='vc-nav-item-close' onClick={() => onClose()} data-test-id='vc-cancel-btn'> X</div>} + </div> + </div> </div> ); } - renderStatus(status) { - switch (status) { - case statusEnum.CHECK_OUT_STATUS: - return ( - <div className='checkout-status-icon'> - <div className='catalog-tile-check-in-status sprite-new checkout-editable-status-icon'></div> - <div className='status-text'> {i18n('CHECKED OUT')} </div> - </div> - ); - case statusEnum.LOCK_STATUS: - return ( - <div className='status-text'> {i18n('LOCKED')} </div> - ); - case statusEnum.CHECK_IN_STATUS: - return ( - <div className='status-text'> {i18n('CHECKED IN')} </div> - ); - case statusEnum.SUBMIT_STATUS: - return ( - <div className='status-text'> {i18n('SUBMITTED')} </div> - ); - default: - return ( - <div className='status-text'> {i18n(status)} </div> - ); - } + submit(callVCAction, version) { + const action = actionsEnum.SUBMIT; + callVCAction(action, version); + } + + revertCheckout(callVCAction, version) { + const action = actionsEnum.UNDO_CHECK_OUT; + callVCAction(action, version); } - checkinCheckoutVersion(callVCAction) { + checkinCheckoutVersion(callVCAction, version) { if (this.props.isCheckedOut) { - this.checkin(callVCAction); + this.checkin(callVCAction, version); } else { - this.checkout(callVCAction); + this.checkout(callVCAction, version); } } - - checkin(callVCAction) { - + checkin(callVCAction, version) { const action = actionsEnum.CHECK_IN; - if (this.props.onSave) { this.props.onSave().then(()=>{ - callVCAction(action); - }); + callVCAction(action, version); + }); }else{ - callVCAction(action); + callVCAction(action, version); } } - - checkout(callVCAction) { + checkout(callVCAction, version) { const action = actionsEnum.CHECK_OUT; - callVCAction(action); + callVCAction(action, version); } +} - submit(callVCAction) { - const action = actionsEnum.SUBMIT; - callVCAction(action); +class ActionButtons extends React.Component { + static propTypes = { + version: React.PropTypes.object, + onSubmit: React.PropTypes.func, + onRevert: React.PropTypes.func, + onSave: React.PropTypes.func, + isLatestVersion: React.PropTypes.bool, + isCheckedIn: React.PropTypes.bool, + isCheckedOut: React.PropTypes.bool, + isFormDataValid: React.PropTypes.bool + }; + 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')]; + const disabled = (isLatestVersion && onCheckinCheckout && status !== statusEnum.LOCK_STATUS) ? false : true; + return ( + <div className='action-buttons'> + <VCButton dataTestId='vc-checkout-btn' onClick={onCheckinCheckout} isDisabled={disabled} + name={checkinBtnIconSvg} tooltipText={checkinCheckoutBtnTitle}/> + {onSubmit && onRevert && + <div className='version-control-buttons'> + <VCButton dataTestId='vc-submit-btn' onClick={onSubmit} isDisabled={!isCheckedIn || !isLatestVersion} + name='version-controller-submit' tooltipText={i18n('Submit')}/> + <VCButton dataTestId='vc-revert-btn' onClick={onRevert} isDisabled={!isCheckedOut || version.label === '0.1' || !isLatestVersion} + name='version-controller-revert' tooltipText={i18n('Revert')}/> + </div> + } + {onSave && + <VCButton dataTestId='vc-save-btn' onClick={() => onSave()} isDisabled={!isCheckedOut || !isFormDataValid || !isLatestVersion} + name='version-controller-save' tooltipText={i18n('Save')}/> + } + </div> + ); } +} - revertCheckout(callVCAction) { - const action = actionsEnum.UNDO_CHECK_OUT; - callVCAction(action); - } +function StatusBarUpdates({status}) { + return ( + <div className='vc-status'> + <span className='status-text'>{i18n(statusBarTextMap[status])}</span> + </div> + ); +} + +function VCButton({name, tooltipText, isDisabled, onClick, dataTestId}) { + let onClickAction = isDisabled ? ()=>{} : onClick; + let disabled = isDisabled ? 'disabled' : ''; + + return ( + <OverlayTrigger placement='top' overlay={<Tooltip id='vc-tooltip'>{tooltipText}</Tooltip>}> + <div disabled={disabled} className='action-buttons-svg'> + <SVGIcon data-test-id={dataTestId} iconClassName={disabled} onClick={onClickAction ? onClickAction : undefined} name={name}/> + </div> + </OverlayTrigger> + ); +} + +function VersionSelector(props) { + let {version = {}, viewableVersions = [], onVersionSwitching} = props; + const includedVersions = viewableVersions.filter(ver => {return ver.id === version.id;}); + return (<div className='version-section-wrapper'> + <select className='version-selector' + onChange={ev => onVersionSwitching && onVersionSwitching({id: ev.target.value, label: ev.target.value})} + value={version.label}> + {viewableVersions && viewableVersions.map(viewVersion => { + return ( + <option key={viewVersion.id} value={viewVersion.id} data-test-id='vc-version-option'>{`V ${viewVersion.label}`}</option> + ); + }) + } + {!includedVersions.length && + <option key={version.id} value={version.id}>{`V ${version.label}`}</option>} + </select> + </div>); } export default VersionController; diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js index 9251fd12c4..9af142433c 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionsEnum = keyMirror({ @@ -36,3 +31,11 @@ export const statusEnum = keyMirror({ PREVIOUS_VERSION: 'READ ONLY' }); +export const statusBarTextMap = keyMirror({ + 'Locked': 'Checked Out', + 'LockedByUser': '', + 'Available': 'Checked In', + 'Final': 'Submitted', + 'READ ONLY': 'Locked' +}); + diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js index de9914454c..e8c12abec3 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import Configuration from 'sdc-app/config/Configuration.js'; import {statusEnum} from './VersionControllerConstants.js'; @@ -25,24 +20,32 @@ import {statusEnum} from './VersionControllerConstants.js'; const VersionControllerUtils = { getCheckOutStatusKindByUserID(status, lockingUser) { - let currentLoginUserID = Configuration.get('ATTUserID'); - let isCheckedOut = currentLoginUserID === lockingUser; + let returnStatus; + let isCheckedOut; + let currentLoginUserID = Configuration.get('UserID'); + if (lockingUser) { + isCheckedOut = currentLoginUserID === lockingUser; + returnStatus = isCheckedOut ? status : statusEnum.LOCK_STATUS; + } else { + isCheckedOut = false; + returnStatus = status; + } return { - status: isCheckedOut ? status : statusEnum.LOCK_STATUS, + status: returnStatus, isCheckedOut }; }, isCheckedOutByCurrentUser(resource) { - let currentLoginUserID = Configuration.get('ATTUserID'); + let currentLoginUserID = Configuration.get('UserID'); return resource.lockingUser !== undefined && resource.lockingUser === currentLoginUserID; }, isReadOnly(resource) { const {version, viewableVersions = []} = resource; const latestVersion = viewableVersions[viewableVersions.length - 1]; - return version !== latestVersion || !VersionControllerUtils.isCheckedOutByCurrentUser(resource); + return version.id !== latestVersion.id || !VersionControllerUtils.isCheckedOutByCurrentUser(resource); } }; diff --git a/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx b/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx index d786aeef8b..40720c39f4 100644 --- a/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx +++ b/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; class ProgressBar extends React.Component { diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx new file mode 100644 index 0000000000..06cb98bbe8 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import uuid from 'uuid-js'; + +export default class SelectActionTable extends React.Component { + + render() { + let {columns, onAdd, isReadOnlyMode, children, onAddItem} = 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' /> + </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' /> + </div> + <div className='select-action-table-body'> + {children} + </div> + </div> + </div> + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx new file mode 100644 index 0000000000..2664c8e944 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import SelectInput from 'nfvo-components/input/SelectInput.jsx'; + +const SelectActionTableCell = ({options, selected, disabled, onChange, clearable = true, placeholder}) => { + return ( + <div className='select-action-table-cell'> + <SelectInput + placeholder={placeholder} + type='select' + value={selected} + data-test-id='select-action-table-dropdown' + disabled={disabled} + onChange={option => onChange(option ? option.value : null)} + clearable={clearable} + options={options} /> + </div> + ); +}; + +export default SelectActionTableCell; diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx new file mode 100644 index 0000000000..17d8a17c09 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import SVGIcon from '../icon/SVGIcon.jsx'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +function tooltip (msg) { + return ( + <Tooltip className='select-action-table-error-tooltip' id='error-tooltip'>{msg}</Tooltip> + ); +}; + +const IconWithOverlay = ({overlayMsg}) => ( + <OverlayTrigger placement='bottom' overlay={tooltip(overlayMsg)}> + <SVGIcon name='error-circle'/> + </OverlayTrigger> +); + +const SelectActionTableRow = ({children, onDelete, hasError, 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'/>} + + </div> +); + +export default SelectActionTableRow; diff --git a/openecomp-ui/src/nfvo-utils/DirectedGraph.js b/openecomp-ui/src/nfvo-utils/DirectedGraph.js new file mode 100644 index 0000000000..197625a013 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/DirectedGraph.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. + */ + +export default class Graph { + constructor() { + this.nodes = {}; + } + + addNode(node) { + this.nodes[node] = []; + } + + hasNode(node) { + return this.nodes.hasOwnProperty(node); + } + + addEdge(firstNode, secondNode, payload) { + if (!this.hasNode(firstNode)) { + this.addNode(firstNode); + } + + if (!this.hasNode(secondNode)) { + this.addNode(secondNode); + } + + this.nodes[firstNode].push({...payload, target: secondNode}); + } + + getEdges(node) { + return this.nodes[node]; + } +}
\ No newline at end of file diff --git a/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js b/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js index 0d27204bef..d58a2454b6 100644 --- a/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js +++ b/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js @@ -1,25 +1,23 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import store from 'sdc-app/AppStore.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import React from 'react'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx'; function showVariablesInMessage(variables, msg) { let regex; @@ -45,6 +43,10 @@ function parseATTExceptionObject(responseJSON) { msg = showVariablesInMessage(variables, msg); } } + else if (responseJSON.uploadDataErrors) { + title = i18n('Error: Upload Data Error'); + msg = (<SubmitErrorResponse validationResponse={{uploadDataErrors: responseJSON.uploadDataErrors}} />); + } else { title = responseJSON.status; msg = responseJSON.message; @@ -60,12 +62,14 @@ var errorResponseHandler = (xhr/*, textStatus, errorThrown*/) => { else { errorData = { title: xhr.statusText, - msg: xhr.responseText + msg: xhr.responseText, }; } store.dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {...errorData} + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + ...errorData + } }); }; diff --git a/openecomp-ui/src/nfvo-utils/KeyMirror.js b/openecomp-ui/src/nfvo-utils/KeyMirror.js index eb50d31e07..220fe07430 100644 --- a/openecomp-ui/src/nfvo-utils/KeyMirror.js +++ b/openecomp-ui/src/nfvo-utils/KeyMirror.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - var keyMirror = function (obj) { var ret = {}; var key; @@ -41,4 +36,4 @@ var keyMirror = function (obj) { return Object.freeze(ret); }; -export default keyMirror; +export default keyMirror;
\ No newline at end of file diff --git a/openecomp-ui/src/nfvo-utils/RestAPIUtil.js b/openecomp-ui/src/nfvo-utils/RestAPIUtil.js index 24734739a2..c878c9e673 100644 --- a/openecomp-ui/src/nfvo-utils/RestAPIUtil.js +++ b/openecomp-ui/src/nfvo-utils/RestAPIUtil.js @@ -1,27 +1,19 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import _extend from 'lodash/extend.js'; -import _clone from 'lodash/clone.js'; -import _defaults from 'lodash/defaults.js'; -import $ from 'jquery'; +import {RestfulAPI} from 'restful-js'; import uuid from 'uuid-js'; import md5 from 'md5'; @@ -30,30 +22,81 @@ import {actionTypes as LoaderConstants} from 'nfvo-components/loader/LoaderConst import Configuration from 'sdc-app/config/Configuration.js'; import errorResponseHandler from './ErrorResponseHandler.js'; -const methodMap = { - 'create': 'POST', - 'update': 'PUT', - 'delete': 'DELETE', - 'read': 'GET' -}; const AUTHORIZATION_HEADER = 'X-AUTH-TOKEN'; const STORAGE_AUTH_KEY = 'sdc-auth-token'; const REQUEST_ID_HEADER = 'X-ECOMP-RequestID'; const CONTENT_MD5_HEADER = 'Content-MD5'; -const namedParam = /{(\w+)}/g; -const queryParamsNames = { - pageStart: 'pageStart', - pageSize: 'pageSize', - sortField: 'sortField', - sortDir: 'sortDir', - filtering: 'filter' -}; + + +function applyMD5Header(options, data) { + if (options.md5) { + let headers = options.headers; + headers[CONTENT_MD5_HEADER] = window.btoa(md5(JSON.stringify(data)).toLowerCase()); + } +} + +function handleResponse(xhr) { + let authToken = xhr.getResponseHeader(AUTHORIZATION_HEADER); + let prevToken = this && this.headers && this.headers[AUTHORIZATION_HEADER]; + if (authToken && authToken !== prevToken) { + if (authToken === 'null') { + localStorage.removeItem(STORAGE_AUTH_KEY); + } else { + localStorage.setItem(STORAGE_AUTH_KEY, authToken); + } + } +} + + +class RestAPIUtil extends RestfulAPI { + + applySecurity(options, data) { + let headers = options.headers || (options.headers = {}); + + let authToken = localStorage.getItem(STORAGE_AUTH_KEY); + if (authToken) { + headers[AUTHORIZATION_HEADER] = authToken; + } + + let attApiHeaders = Configuration.get('ATTApiHeaders'), + attUidHeader = attApiHeaders && attApiHeaders.userId; + if (attUidHeader) { + headers[attUidHeader.name] = attUidHeader.value; + } + + headers[REQUEST_ID_HEADER] = uuid.create().toString(); + applyMD5Header(options, data); + } + + handleRequest(url, type, options = {}, data){ + let success = options.success; + options.success = function (resp, textStatus, xhr) { + handleResponse.call(this, xhr); + if (success) { + success.call(options.context, {...resp}, textStatus, xhr); + } + }; + + if (DEBUG) { + console.log('--> Making REST call (' + type + '): ' + url); + } + return super.handleRequest(url, type, options, data); + } + +} + +const instance = new RestAPIUtil({ + errorResponseHandler, + ajaxStartHandler: () => store.dispatch({type: LoaderConstants.SHOW}), + ajaxStopHandler: () => store.dispatch({type: LoaderConstants.HIDE}) +}); + // jQuery binary transport to download files through XHR // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ // https://github.com/henrya/js-jquery/tree/master/BinaryTransport -$.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) { +instance.$.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) { // check for conditions and support for blob / arraybuffer response type if (window.FormData && ((options.dataType && (options.dataType === 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || @@ -62,19 +105,19 @@ $.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) { return { // create new XMLHttpRequest send: function (headers, callback) { - // setup all variables - var xhr = new XMLHttpRequest(), + // setup all letiables + let xhr = new XMLHttpRequest(), url = options.url, type = options.type, async = options.async || true, - // blob or arraybuffer. Default is blob + // blob or arraybuffer. Default is blob dataType = options.responseType || 'blob', data = options.data || null, username = options.username || null, password = options.password || null; xhr.addEventListener('load', function () { - var data = {}; + let data = {}; data[options.dataType] = xhr.response; // make callback and send data callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); @@ -83,7 +126,7 @@ $.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) { xhr.open(type, url, async, username, password); // setup custom headers - for (var i in headers) { + for (let i in headers) { xhr.setRequestHeader(i, headers[i]); } @@ -96,193 +139,4 @@ $.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) { } }); -$(document).ajaxStart(()=> store.dispatch({type: LoaderConstants.SHOW})); -$(document).ajaxStop(()=> store.dispatch({type: LoaderConstants.HIDE})); - -function urlError() { - throw new Error('A "url" property or function must be specified'); -}; - -export function makeQueryParams(options) { - var qParams = {}; - if (options.pagination) { - qParams[queryParamsNames.pageStart] = options.pagination.pageStart; - qParams[queryParamsNames.pageSize] = options.pagination.pageSize; - } - if (options.sorting) { - qParams[queryParamsNames.sortField] = options.sorting.sortField; - qParams[queryParamsNames.sortDir] = options.sorting.sortDir; - } - if (options.filtering) { - qParams[queryParamsNames.filtering] = JSON.stringify(options.filtering); - } - - return _defaults(qParams, options.qParams); -} - -function appendQueryParam(p, value) { - var str = ''; - - if (value instanceof Array) { - if (value.length === 1) { - str = appendQueryParam(p, value[0]); - } else if (value.length > 1) { - str = appendQueryParam(p, value.shift()) + '&' + appendQueryParam(p, value); - } - } else { - str = p + '=' + encodeURIComponent(value); - } - - return str; -} - -function appendQueryString(url, qParams) { - var str = ''; - for (var param in qParams) { - str += (str ? '&' : '') + appendQueryParam(param, qParams[param]); - } - return url + (str ? '?' : '') + str; -} - -function composeURL(baseUrl, options) { - var url = baseUrl || urlError(); - if (options.url) { - delete options.url; - } - - var qParams = makeQueryParams(options); - url = appendQueryString(url, qParams); - - var matches = url.match(namedParam); - if (matches) { - for (var i = 0; i < matches.length; i++) { - var param = matches[i].substring(1, matches[i].length - 1); - var value = (options.params && options.params[param]); - - if (value === undefined) { - value = options[param]; - } - url = url.replace(matches[i], encodeURIComponent(value)); - } - } - - return url; -} - -function applyMD5Header(options, data) { - if (options.md5) { - let headers = options.headers; - headers[CONTENT_MD5_HEADER] = window.btoa(md5(JSON.stringify(data)).toLowerCase()); - } -} - -function applySecurity(options, data) { - var headers = options.headers || (options.headers = {}); - - var authToken = localStorage.getItem(STORAGE_AUTH_KEY); - if (authToken) { - headers[AUTHORIZATION_HEADER] = authToken; - } - - var attApiHeaders = Configuration.get('ATTApiHeaders'), - attUidHeader = attApiHeaders && attApiHeaders.userId; - if (attUidHeader) { - headers[attUidHeader.name] = attUidHeader.value; - } - - headers[REQUEST_ID_HEADER] = uuid.create().toString(); - - applyMD5Header(options, data); -} - -function handleResponse(options) { - var authToken = options.xhr.getResponseHeader(AUTHORIZATION_HEADER); - var prevToken = options.headers && options.headers[AUTHORIZATION_HEADER]; - if (authToken && authToken !== prevToken) { - if (authToken === 'null') { - localStorage.removeItem(STORAGE_AUTH_KEY); - } else { - localStorage.setItem(STORAGE_AUTH_KEY, authToken); - } - } -} - -function sync(baseUrl, method, options, data) { - - options = options ? _clone(options) : {}; - - var type = methodMap[method]; - _defaults(options || (options = {})); - var params = { - type: type, - dataType: 'json' - }; - params.url = composeURL(baseUrl, options); - - if ((method === 'create' || method === 'update') && data instanceof FormData) { - params.contentType = 'multipart/form-data'; - params.data = data; - } - else if (method === 'create' || method === 'update') { - params.contentType = 'application/json'; - params.data = JSON.stringify(data); - } - - if (params.type !== 'GET') { - params.processData = false; - } - var success = options.success; - options.success = function (resp) { - if (success) { - handleResponse(options); - success.call(options.context, _clone(resp), resp, options); - } - }; - - options.error = options.error || errorResponseHandler; - - if (typeof options.progressCallback === 'function' && options.fileSize) { - const {fileSize} = options; - options.xhrFields = { - // add listener to XMLHTTPRequest object directly for progress (jquery doesn't have this yet) - onprogress: function (progress) { - // calculate upload progress - let percentage = Math.floor((progress.loaded / fileSize) * 100); - // log upload progress to console - //console.log('progress', percentage); - options.progressCallback(percentage); - if (percentage === 100) { - console.log('DONE!'); - } - } - }; - } - - applySecurity(options, data); - - if (DEBUG) { - console.log('--> Making REST call (' + type + '): ' + params.url); - } - var xhr = options.xhr = $.ajax(_extend(params, options)); - return xhr; -} - -export default { - - fetch(baseUrl, options) { - return sync(baseUrl, 'read', options); - }, - - save(baseUrl, data, options) { - return sync(baseUrl, 'update', options, data); - }, - - create(baseUrl, data, options) { - return sync(baseUrl, 'create', options, data); - }, - - destroy(baseUrl, options) { - return sync(baseUrl, 'delete', options); - } - -}; +export default instance; diff --git a/openecomp-ui/src/nfvo-utils/UUID.js b/openecomp-ui/src/nfvo-utils/UUID.js index 314c98ba6f..e1d4c54b1f 100644 --- a/openecomp-ui/src/nfvo-utils/UUID.js +++ b/openecomp-ui/src/nfvo-utils/UUID.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import UUID from 'uuid-js'; let toCustomUUID = (uuid) => { diff --git a/openecomp-ui/src/nfvo-utils/Validator.js b/openecomp-ui/src/nfvo-utils/Validator.js new file mode 100644 index 0000000000..708179e9fb --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/Validator.js @@ -0,0 +1,110 @@ +/*! + * 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 * as ValidatorJS from 'validator'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +class Validator { + static get globalValidationFunctions() { + return { + required: 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), + numeric: value => { + if (value === '') { + // to allow empty value which is not zero + return true; + } + return ValidatorJS.isNumeric(value); + }, + maximum: (value, maxValue) => value <= maxValue, + minimum: (value, minValue) => value >= minValue, + maximumExclusive: (value, maxValue) => value < maxValue, + minimumExclusive: (value, minValue) => value > minValue, + alphanumeric: value => ValidatorJS.isAlphanumeric(value), + alphanumericWithSpaces: value => ValidatorJS.isAlphanumeric(value.replace(/ /g, '')), + validateName: value => ValidatorJS.isAlphanumeric(value.replace(/\s|\.|\_|\-/g, ''), 'en-US'), + validateVendorName: value => ValidatorJS.isAlphanumeric(value.replace(/[\x7F-\xFF]|\s/g, ''), 'en-US'), + 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) + }; + } + + static get globalValidationMessagingFunctions() { + return { + required: () => i18n('Field is required'), + 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}), + 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()}), + 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.'), + validateVendorName: ()=> i18n('Field value should contain English letters digits and spaces only.'), + freeEnglishText: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), + 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.') + }; + } + + static validateItem(value, data, type) { + let validationFunc = this.globalValidationFunctions[type]; + const isValid = validationFunc(value, data); + let errorText = ''; + if (!isValid) { + errorText = this.globalValidationMessagingFunctions[type](value, data); + } + return { + isValid, + errorText + }; + } + + static validate(fieldName, value, validations, state, customValidations) { + let result = { isValid: true, errorText: '' }; + for (let validation of validations) { + result = this.validateItem(value, validation.data, validation.type); + if (!result.isValid) { + return result; + } + } + if (customValidations) { + let validationFunc = customValidations[fieldName]; + if (validationFunc) { + result = validationFunc(value, state); + } + } + return result; + } + + static isItemNameAlreadyExistsInList({itemId, itemName, list}) { + return list[itemName] && list[itemName] !== itemId; + } +} + +export default Validator; diff --git a/openecomp-ui/src/nfvo-utils/i18n/i18n.js b/openecomp-ui/src/nfvo-utils/i18n/i18n.js index 64587713b7..4d03ddb8dd 100644 --- a/openecomp-ui/src/nfvo-utils/i18n/i18n.js +++ b/openecomp-ui/src/nfvo-utils/i18n/i18n.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import IntlObj from 'intl'; import IntlMessageFormatObj from 'intl-messageformat'; import IntlRelativeFormatObj from 'intl-relativeformat'; diff --git a/openecomp-ui/src/nfvo-utils/json/JSONPointer.js b/openecomp-ui/src/nfvo-utils/json/JSONPointer.js index a6e8198537..f4c0d4ed59 100644 --- a/openecomp-ui/src/nfvo-utils/json/JSONPointer.js +++ b/openecomp-ui/src/nfvo-utils/json/JSONPointer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - const JSONPointer = { extractParentPointer(pointer) { @@ -29,7 +24,7 @@ const JSONPointer = { return lastPart; }, - extractParts(pointer) { + extractParts(pointer = '') { return pointer.split('/').slice(1) .map(part => part.replace(/~1/g, '/')) .map(part => part.replace(/~0/g, '~')); diff --git a/openecomp-ui/src/nfvo-utils/json/JSONSchema.js b/openecomp-ui/src/nfvo-utils/json/JSONSchema.js index 8c7d8cf9aa..3b3a9bf7b4 100644 --- a/openecomp-ui/src/nfvo-utils/json/JSONSchema.js +++ b/openecomp-ui/src/nfvo-utils/json/JSONSchema.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - // import Ajv from 'ajv'; import cloneDeep from 'lodash/cloneDeep.js'; import JSONPointer from './JSONPointer.js'; @@ -40,6 +35,108 @@ export default class JSONSchema { return data; } + // array of names of validation functions + setSupportedValidationFunctions(supportedValidationFunctions) { + this._supportedValidationFunctions = supportedValidationFunctions; + } + + /* FYI - I was going to support "required" but then found out that server never sends it in its schema (it was a business decision. so leaving the code commented for now */ + flattenSchema(supportedValidationFunctions) { + if (supportedValidationFunctions) { this.setSupportedValidationFunctions(supportedValidationFunctions); } + let genericFieldInfo = {}; + if (this._schema && this._schema.properties) { + this.travelProperties(this._schema.properties, genericFieldInfo/*, this._schema.required*/); + } + return {genericFieldInfo}; + } + + extractGenericFieldInfo(item) { + let validationsArr = []; + let additionalInfo = { isValid: true, errorText: ''}; + for (let value in item) { + if (this._supportedValidationFunctions.includes(value)) { + let validationItem = this.extractValidations(item, value); + validationsArr[validationsArr.length] = validationItem; + } else { + let enumResult = this.extractEnum(item, value); + if (enumResult !== null) { + additionalInfo.enum = enumResult; + } + else { + additionalInfo[value] = item[value]; + } + /*if (required.includes (property)) { + additionalInfo[value].isRequired = true ; + }*/ + } + } + + additionalInfo.validations = validationsArr; + return additionalInfo; + } + + extractValidations(item, value) { + let validationItem; + let data = item[value]; + if (value === 'maximum') { + if (item.exclusiveMaximum) { + value = 'maximumExclusive'; + } + } + if (value === 'minimum') { + if (item.exclusiveMinimum) { + value = 'minimumExclusive'; + } + } + validationItem = {type: value, data: data}; + return validationItem; + } + + extractEnum(item, value) { + let enumResult = null; + if (value === 'type' && item[value] === 'array') { + let items = item.items; + if (items && items.enum && items.enum.length > 0) { + let values = items.enum + .filter(value => value) + .map(value => ({enum: value, title: value})); + enumResult = values; + } + } + else if (value === 'enum') { + let items = item[value]; + if (items && items.length > 0) { + let values = items + .filter(value => value) + .map(value => ({enum: value, title: value})); + enumResult = values; + } + } + return enumResult; + } + + travelProperties(properties, genericFieldDefs, /*required = [],*/ pointer = ''){ + let newPointer = pointer; + for (let property in properties) { + newPointer = newPointer ? newPointer + '/' + property : property; + if (properties[property].properties) { + this.travelProperties(properties[property].properties, genericFieldDefs /*, properties[property].required*/, newPointer); + } + else if (properties[property].$ref){ + let fragment = this._getSchemaFragmentByRef(properties[property].$ref); + if (fragment.properties) { + this.travelProperties(fragment.properties, genericFieldDefs /*, properties[property].required*/, newPointer); + } else { + genericFieldDefs[newPointer] = this.extractGenericFieldInfo(fragment.properties); + } + } + else { + genericFieldDefs[newPointer] = this.extractGenericFieldInfo(properties[property]); + } + newPointer = pointer; + } + } + getTitle(pointer) { return this._getSchemaFragment(pointer).title; } @@ -73,12 +170,12 @@ export default class JSONSchema { getMaxValue(pointer) { const fragment = this._getSchemaFragment(pointer); - return fragment && fragment.maximum; + return fragment && fragment.exclusiveMaximum ? fragment.maximum - 1 : fragment.maximum; } getMinValue(pointer) { const fragment = this._getSchemaFragment(pointer); - return fragment && fragment.minimum; + return fragment && fragment.exclusiveMinimum ? fragment.minimum : fragment.minimum; } isString(pointer) { diff --git a/openecomp-ui/src/nfvo-utils/sortByStringProperty.js b/openecomp-ui/src/nfvo-utils/sortByStringProperty.js new file mode 100644 index 0000000000..b415dd7e07 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/sortByStringProperty.js @@ -0,0 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +export default function sortByStringProperty(array, property) { + return [...array].sort((a, b) => a[property].toLowerCase().localeCompare(b[property].toLowerCase())); +} diff --git a/openecomp-ui/src/sdc-app/AppStore.js b/openecomp-ui/src/sdc-app/AppStore.js index 0abcaac3fe..9157c340a3 100644 --- a/openecomp-ui/src/sdc-app/AppStore.js +++ b/openecomp-ui/src/sdc-app/AppStore.js @@ -1,46 +1,41 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ -import {combineReducers, createStore} from 'redux'; +import {combineReducers, createStore, applyMiddleware, compose} from 'redux'; import onBoardingReducersMap from './onboarding/OnboardingReducersMap.js'; import flowsReducersMap from './flows/FlowsReducersMap.js'; -import notificationReducer from 'nfvo-components/notifications/NotificationReducer.js'; import loaderReducer from 'nfvo-components/loader/LoaderReducer.js'; -import uploadScreenReducer from 'sdc-app/heatvalidation/UploadScreenReducer.js'; -import SoftwareProductAttachmentsReducer from 'sdc-app/heatvalidation/attachments/AttachmentsReducer'; +import globalModalReducer from 'nfvo-components/modal/GlobalModalReducer.js'; +const thunk = store => next => action => + typeof action === 'function' ? + action(store.dispatch, store.getState) : + next(action); + +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; export const storeCreator = (initialState) => createStore(combineReducers({ // on-boarding reducers ...onBoardingReducersMap, // flows reducers ...flowsReducersMap, - - // heat validation stand-alone app - uploadScreen: combineReducers({ - upload: uploadScreenReducer, - attachments: SoftwareProductAttachmentsReducer - }), - notification: notificationReducer, + modal: globalModalReducer, loader: loaderReducer -}), initialState, window.devToolsExtension ? window.devToolsExtension() : undefined); +}), initialState, composeEnhancers(applyMiddleware(thunk))); + const store = storeCreator(); diff --git a/openecomp-ui/src/sdc-app/Application.jsx b/openecomp-ui/src/sdc-app/Application.jsx index 5cb385b61c..10e866a602 100644 --- a/openecomp-ui/src/sdc-app/Application.jsx +++ b/openecomp-ui/src/sdc-app/Application.jsx @@ -1,6 +1,21 @@ +/*! + * 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 {Provider} from 'react-redux'; -import NotificationModal from 'nfvo-components/notifications/NotificationModal.jsx'; +import GlobalModal from 'nfvo-components/modal/GlobalModal.js'; import Loader from 'nfvo-components/loader/Loader.jsx'; import store from './AppStore.js'; @@ -10,7 +25,7 @@ class Application extends React.Component { return ( <Provider store={store}> <div> - <NotificationModal /> + <GlobalModal/> {this.props.children} <Loader /> </div> diff --git a/openecomp-ui/src/sdc-app/ModulesOptions.jsx b/openecomp-ui/src/sdc-app/ModulesOptions.jsx index 4f66e579d1..61d19d9e7a 100644 --- a/openecomp-ui/src/sdc-app/ModulesOptions.jsx +++ b/openecomp-ui/src/sdc-app/ModulesOptions.jsx @@ -1,6 +1,21 @@ +/*! + * 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 Input from 'react-bootstrap/lib/Input.js'; +import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; import LicenseModelActionHelper from './onboarding/licenseModel/LicenseModelActionHelper.js'; import LicenseAgreementListEditor from './onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; @@ -13,7 +28,7 @@ import EntitlementPoolsListEditor from './onboarding/licenseModel/entitlementPoo import EntitlementPoolsActionHelper from './onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; import SoftwareProductLandingPage from './onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js'; import SoftwareProductDetails from './onboarding/softwareProduct/details/SoftwareProductDetails.js'; -import OnboardingCatalog from './onboarding/OnboardingCatalog.js'; +import Onboard from './onboarding/onboard/Onboard.js'; import SoftwareProductActionHelper from './onboarding/softwareProduct/SoftwareProductActionHelper.js'; import FlowsListEditor from './flows/FlowsListEditor.js'; import FlowsActions from './flows/FlowsActions.js'; @@ -125,7 +140,7 @@ class ModuleOptions extends React.Component { return <SoftwareProductDetails licenseModelId={licenseModelId}/>; case 'OnboardingCatalog': this.props.onOnboardingCatalog(); - return <OnboardingCatalog/>; + return <Onboard/>; case 'Flows': this.props.onFlowsListEditor(); return <FlowsListEditor/>; diff --git a/openecomp-ui/src/sdc-app/Test.jsx b/openecomp-ui/src/sdc-app/Test.jsx deleted file mode 100644 index dd45e39eca..0000000000 --- a/openecomp-ui/src/sdc-app/Test.jsx +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react'; -import Tabs from 'react-bootstrap/lib/Tabs.js'; -import Tab from 'react-bootstrap/lib/Tab.js'; -import Button from 'react-bootstrap/lib/Button.js'; -import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; -import DropdownButton from 'react-bootstrap/lib/DropdownButton.js'; -import MenuItem from 'react-bootstrap/lib/MenuItem.js'; - -import Modal from 'nfvo-components/modal/Modal.jsx'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import ToggleInput from 'nfvo-components/input/ToggleInput.jsx'; - -export default class Test extends React.Component { - - render() { - return ( - <div> - <Tabs defaultActiveKey={2}> - <Tab eventKey={1} title='Tab 1'>Tab 1 content</Tab> - <Tab eventKey={2} title='Tab 2'>Tab 2 content</Tab> - <Tab eventKey={3} title='Tab 3' disabled>Tab 3 content</Tab> - </Tabs> - <div style={{marginTop: 20, marginBottom: 20}}></div> - <Button>Default</Button> - <span style={{marginLeft: 20}}></span> - <Button bsStyle='primary'>Primary</Button> - <span style={{marginLeft: 20}}></span> - <Button bsStyle='success'>Success</Button> - <span style={{marginLeft: 20}}></span> - <Button bsStyle='info'>Info</Button> - <span style={{marginLeft: 20}}></span> - <Button bsStyle='warning'>Warning</Button> - <span style={{marginLeft: 20}}></span> - <Button bsStyle='danger'>Danger</Button> - <span style={{marginLeft: 20}}></span> - <Button bsStyle='link'>Link</Button> - <div style={{marginTop: 20, marginBottom: 20}}></div> - <ButtonGroup> - <Button>Left</Button> - <Button>Middle</Button> - <Button>Right</Button> - </ButtonGroup> - <div style={{marginTop: 20, marginBottom: 20}}></div> - <DropdownButton title='title' id='dropdown-basic'> - <MenuItem eventKey='1'>Action</MenuItem> - <MenuItem eventKey='2'>Another action</MenuItem> - <MenuItem eventKey='3' active>Active Item</MenuItem> - <MenuItem divider/> - <MenuItem eventKey='4'>Separated link</MenuItem> - </DropdownButton> - - <div style={{marginTop: 20, marginBottom: 20}}></div> - <Modal show={false}> - <Modal.Header closeButton> - <Modal.Title>Modal title</Modal.Title> - </Modal.Header> - - <Modal.Body> - One fine body... - </Modal.Body> - - <Modal.Footer> - <Button>Close</Button> - <Button bsStyle='primary'>Save changes</Button> - </Modal.Footer> - - </Modal> - - <div style={{marginTop: 20, marginBottom: 20}}></div> - - <ValidationForm> - <ValidationInput - type='text' - label='Required' - placeholder='Enter text' - validations={{required: true}}/> - <ValidationInput - type='text' - label='Text' - placeholder='Enter text' - validations={{required: true, minLength:5}}/> - <ValidationInput - type='email' - label='Email Address' - placeholder='Enter email' - validations={{required: true, email: true}}/> - <ValidationInput type='password' label='Password'/> - <ValidationInput type='file' label='File' help='[Optional] Block level help text'/> - <ValidationInput type='checkbox' label='Checkbox2' name='ziv'/> - <ValidationInput type='radio' label='Radio' name='zzz'/> - <ValidationInput type='select' label='Select' placeholder='select'> - <option value='select'>select</option> - <option value='other'>...</option> - </ValidationInput> - <ValidationInput type='select' label='Multiple Select' multiple> - <option value='select'>select (multiple)</option> - <option value='other'>...</option> - </ValidationInput> - <ValidationInput type='textarea' label='Text Area' placeholder='textarea'/> - <ToggleInput value={true}/> - <ToggleInput /> - <ToggleInput label='ziv' value={true}/> - <ToggleInput label='ziv'/> - </ValidationForm> - </div> - ); - } - - doSomething(a) { - if (a) { - this.doSomething2(); - } - else { - return 1; - } - } - - doSomething2() { - return 2; - } -} diff --git a/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js b/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js new file mode 100644 index 0000000000..51dfcf9a67 --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js @@ -0,0 +1,91 @@ +/*! + * 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 as commonActionTypes} from 'sdc-app/common/reducers/PlainDataReducerConstants.js'; +import {actionTypes as qcommonActionTypes} from 'sdc-app/common/reducers/JSONSchemaReducerConstants.js'; + +class ValidationHelper { + + static dataChanged(dispatch, {deltaData, formName, customValidations = {}}){ + dispatch({ + type: commonActionTypes.DATA_CHANGED, + deltaData, + formName, + customValidations + }); + } + + static validateForm(dispatch, formName){ + dispatch({ + type: commonActionTypes.VALIDATE_FORM, + formName + }); + } + + static validateData(dispatch, {formName, data}) { + dispatch({ + type: commonActionTypes.VALIDATE_DATA, + formName, + data + }); + } + + static qValidateData(dispatch, {data, qName, customValidations = {}}) { + dispatch({ + type: qcommonActionTypes.VALIDATE_DATA, + data, + qName, + customValidations + }); + } + + static qValidateForm(dispatch, qName){ + dispatch({ + type: qcommonActionTypes.VALIDATE_FORM, + qName + }); + } + + static qDataChanged(dispatch, {deltaData, qName, customValidations = {}}){ + dispatch({ + type: qcommonActionTypes.DATA_CHANGED, + deltaData, + qName, + customValidations + }); + } + + static qDataLoaded(dispatch, {qName, response: {qdata, qschema}}) { + dispatch({ + type: qcommonActionTypes.DATA_LOADED, + payload: { + qdata, + qschema + }, + qName + }); + } + + static checkFormValid(genericFieldInfo) { + for (let field in genericFieldInfo) { + if (!genericFieldInfo[field].isValid) { + return false; + } + } + return true; + } +} + +export default ValidationHelper; diff --git a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js new file mode 100644 index 0000000000..548e0cfc9c --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js @@ -0,0 +1,31 @@ +/*! + * 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 SoftwareProductCreation from 'sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js'; +import LicenseModelCreation from 'sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js'; +import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx'; + +export const modalContentMapper = { + SOFTWARE_PRODUCT_CREATION: 'SOFTWARE_PRODUCT_CREATION', + LICENSE_MODEL_CREATION: 'LICENSE_MODEL_CREATION', + SUMBIT_ERROR_RESPONSE: 'SUMBIT_ERROR_RESPONSE' +}; + +export const modalContentComponents = { + SUMBIT_ERROR_RESPONSE: SubmitErrorResponse, + SOFTWARE_PRODUCT_CREATION: SoftwareProductCreation, + LICENSE_MODEL_CREATION: LicenseModelCreation, +};
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js b/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js new file mode 100644 index 0000000000..35b2f936ce --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js @@ -0,0 +1,145 @@ +/*! + * 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 './JSONSchemaReducerConstants.js'; +import Validator from 'nfvo-utils/Validator.js'; +import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; +import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; +import forOwn from 'lodash/forOwn.js'; +import isArray from 'lodash/isArray.js'; + + +function flattenData(data, result, pointer = '') { + let newPointer = pointer; + if (typeof data === 'object' && !isArray(data)) { + for (let i in data) { + newPointer = newPointer ? newPointer + '/' + i : i; + flattenData(data[i], result, newPointer); + newPointer = pointer; + } + } else { + result[newPointer] = data; + } +} + +function updateSchemaDataAndValidateReducer (state = {}, action, questionnaireName) { + let genericFieldInfoClone; + switch (action.type) { + case actionTypes.DATA_LOADED: + if (questionnaireName !== action.qName) {return {...state};} + const schema = action.payload.qschema; + let schemaLoader = new JSONSchema(); + schemaLoader.setSchema(schema); + schemaLoader.setSupportedValidationFunctions(Object.keys(Validator.globalValidationFunctions)); + let {genericFieldInfo} = schemaLoader.flattenSchema(); + + let data = action.payload.qdata; + let dataMap = {}; + flattenData(data, dataMap); + + return { + ...state, + qdata: action.payload.qdata, // the original hierarchical data. to be used for submit and save + qgenericFieldInfo : genericFieldInfo, // information about the fields that the view will require and reducer will need, such as validations, enum to use, etc. + dataMap // flattened schema data for ease of use + }; + + case actionTypes.DATA_CHANGED: + let changedData = action.deltaData; + if (questionnaireName !== action.qName) {return {...state};} + + genericFieldInfoClone = {...state.qgenericFieldInfo}; + let qDataClone = {...state.qdata}; + let dataMapClone = {...state.dataMap}; + + forOwn(changedData,(value, key) => { + if (state.qgenericFieldInfo[key]) { + let result = Validator.validate(key, value, state.qgenericFieldInfo[key].validations, state, action.customValidations); + genericFieldInfoClone[key] = {...genericFieldInfoClone[key], isValid: result.isValid, errorText: result.errorText}; + qDataClone = JSONPointer.setValue(state.qdata, '/' + key, value); + dataMapClone[key] = value; + } + }); + + return { + ...state, + qdata: qDataClone, + dataMap: dataMapClone, + qgenericFieldInfo: genericFieldInfoClone + }; + + case actionTypes.VALIDATE_DATA: + let specificFields = action.data; + if (questionnaireName !== action.qName) {return {...state};} + genericFieldInfoClone = {...state.qgenericFieldInfo}; + forOwn(specificFields,(value, key) => { + let result = Validator.validate(key, value, state.qgenericFieldInfo[key].validations, state, action.customValidations); + genericFieldInfoClone[key] = {...genericFieldInfoClone[key], isValid: result.isValid, errorText: result.errorText}; + }); + return { + ...state, + formReady: null, + qgenericFieldInfo: genericFieldInfoClone + }; + + case actionTypes.VALIDATE_FORM: + if (questionnaireName !== action.qName) {return {...state};} + genericFieldInfoClone = {...state.qgenericFieldInfo}; + let formReady = true; + forOwn(state.qgenericFieldInfo,(value, key) => { + let val = state.data[key] ? state.data[key] : ''; + let result = Validator.validate(key, val, state.qgenericFieldInfo[key].validations, state, {}); + genericFieldInfoClone[key] = {...genericFieldInfoClone[key], isValid: result.isValid, errorText: result.errorText}; + if (!result.isValid) { + formReady = false; + } + }); + return { + ...state, + formReady, + qgenericFieldInfo: genericFieldInfoClone + }; + + default: + return state; + } +}; + +export function createJSONSchemaReducer(questionnaireName) { + return (state = {}, action) => { + return updateSchemaDataAndValidateReducer(state, action, questionnaireName); + }; +}; + +export function createComposedJSONSchemaReducer(questionnaireName, additionalActionsReducer) { + return (state = {}, action) => { + if(action.type === actionTypes.VALIDATE_DATA || + action.type === actionTypes.VALIDATE_FORM || + action.type === actionTypes.DATA_CHANGED || + action.type === actionTypes.DATA_LOADED + ) { + return updateSchemaDataAndValidateReducer(state, action, questionnaireName); + } else { + return additionalActionsReducer(state, action); + } + }; +}; + + + + + + + diff --git a/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducerConstants.js b/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducerConstants.js new file mode 100644 index 0000000000..6007b878dd --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducerConstants.js @@ -0,0 +1,23 @@ +/*! + * 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({ + DATA_LOADED: null, + DATA_CHANGED: null, + VALIDATE_FORM: null, + VALIDATE_DATA: null +}); diff --git a/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js new file mode 100644 index 0000000000..0bbb5e63be --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js @@ -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 {actionTypes} from './PlainDataReducerConstants.js'; +import Validator from 'nfvo-utils/Validator.js'; +import forOwn from 'lodash/forOwn.js'; + +function updateDataAndValidateReducer(state = {}, action) { + let genericFieldInfoCopy; + switch (action.type) { + case actionTypes.DATA_CHANGED: + let changed = action.deltaData; + if (!action.formName || (state.formName !== action.formName)) {return {...state};} + genericFieldInfoCopy = {...state.genericFieldInfo}; + forOwn(changed,(value, key) => { + if (state.genericFieldInfo[key]) { + let result = Validator.validate(key, value, state.genericFieldInfo[key].validations, state, action.customValidations); + genericFieldInfoCopy[key] = {...genericFieldInfoCopy[key], isValid: result.isValid, errorText: result.errorText}; + } + }); + return { + ...state, + formReady: null, + data: { + ...state.data, + ...action.deltaData + }, + genericFieldInfo: genericFieldInfoCopy + }; + case actionTypes.VALIDATE_FORM: + if (!action.formName || (state.formName !== action.formName)) {return {...state};} + genericFieldInfoCopy = {...state.genericFieldInfo}; + let formReady = true; + forOwn(state.genericFieldInfo,(value, key) => { + let val = state.data && state.data[key] ? state.data[key] : ''; + let result = Validator.validate(key, val, state.genericFieldInfo[key].validations, state, {}); + genericFieldInfoCopy[key] = {...genericFieldInfoCopy[key], isValid: result.isValid, errorText: result.errorText}; + if (!result.isValid) { + formReady = false; + } + }); + return { + ...state, + formReady, + genericFieldInfo: genericFieldInfoCopy + }; + case actionTypes.VALIDATE_DATA: + let specificFields = action.data; + if (!action.formName || (state.formName !== action.formName)) {return {...state};} + genericFieldInfoCopy = {...state.genericFieldInfo}; + forOwn(specificFields,(value, key) => { + let result = Validator.validate(key, value, state.genericFieldInfo[key].validations, state, action.customValidations); + genericFieldInfoCopy[key] = {...genericFieldInfoCopy[key], isValid: result.isValid, errorText: result.errorText}; + }); + return { + ...state, + formReady: null, + genericFieldInfo: genericFieldInfoCopy + }; + default: + return state; + } +}; + +export function createPlainDataReducer(loadReducer) { + return (state = {}, action) => { + if(action.type === actionTypes.VALIDATE_DATA || + action.type === actionTypes.VALIDATE_FORM || + action.type === actionTypes.DATA_CHANGED + ) { + return updateDataAndValidateReducer(state, action); + } else { + return loadReducer(state, action); + } + }; +}; + + + + + + diff --git a/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducerConstants.js b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducerConstants.js new file mode 100644 index 0000000000..135361dd20 --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducerConstants.js @@ -0,0 +1,22 @@ +/*! + * 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({ + DATA_CHANGED: null, + VALIDATE_FORM: null, + VALIDATE_DATA: null +}); diff --git a/openecomp-ui/src/sdc-app/config/Configuration.js b/openecomp-ui/src/sdc-app/config/Configuration.js index 4bbe07864f..227e36995b 100644 --- a/openecomp-ui/src/sdc-app/config/Configuration.js +++ b/openecomp-ui/src/sdc-app/config/Configuration.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import configData from './config.json'; class Configuration { @@ -43,19 +38,17 @@ class Configuration { setATTApiHeaders(ATTApiHeaders) { this.set('ATTApiHeaders', ATTApiHeaders); - let {userId: {value: ATTUserID} = {}} = ATTApiHeaders; - this.set('ATTUserID', ATTUserID); + let {userId: {value: UserID} = {}} = ATTApiHeaders; + this.set('UserID', UserID); } - } const configuration = new Configuration(); (function setDefaultRestPrefixes(configuration) { - configuration.set('restPrefix', configuration.get('defaultRestPrefix')); configuration.set('restATTPrefix', configuration.get('defaultRestATTPrefix')); - + configuration.set('appContextPath', configuration.get('appContextPath')); })(configuration); diff --git a/openecomp-ui/src/sdc-app/config/config.json b/openecomp-ui/src/sdc-app/config/config.json index 4127e0c12e..2725cf1310 100644 --- a/openecomp-ui/src/sdc-app/config/config.json +++ b/openecomp-ui/src/sdc-app/config/config.json @@ -2,7 +2,7 @@ "pageSize": 25, "version": "9.4", "build": "dev", - + "appContextPath" : "/onboarding", "defaultRestPrefix": "/onboarding-api", "defaultRestATTPrefix": "/sdc1/feProxy/rest" } diff --git a/openecomp-ui/src/sdc-app/flows/FlowsActions.js b/openecomp-ui/src/sdc-app/flows/FlowsActions.js index b8772edb08..5a751064df 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsActions.js +++ b/openecomp-ui/src/sdc-app/flows/FlowsActions.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes, enums} from './FlowsConstants.js'; @@ -59,7 +54,8 @@ function decodeContent(base64Contents) { sequenceDiagramModel = payload.data || payload; sequenceDiagramModel = sequenceDiagramModel.model || sequenceDiagramModel; - } else if (payload.VERSION.major === 1) { + } + else if (payload.VERSION.major === 1) { description = payload.description; sequenceDiagramModel = payload.sequenceDiagramModel; } @@ -80,7 +76,7 @@ function createOrUpdate(flowData) { description: flowData.description }; - return RestAPIUtil.create( + return RestAPIUtil.post( baseUrl(flowData.serviceID, flowData.uniqueId), createOrUpdateRequest, {md5: true} @@ -89,11 +85,13 @@ function createOrUpdate(flowData) { const FlowsActions = Object.freeze({ - fetchFlowArtifacts(dispatch, {artifacts, diagramType, participants, serviceID}) { + fetchFlowArtifacts(dispatch, {artifacts, diagramType, participants, serviceID, readonly}) { let results = []; if (!Object.keys(artifacts).length) { - dispatch({type: actionTypes.FLOW_LIST_LOADED, results, participants, serviceID, diagramType}); - FlowsActions.openFlowDetailsEditor(dispatch); + dispatch({type: actionTypes.FLOW_LIST_LOADED, results, participants, serviceID, diagramType, readonly}); + if (!readonly) { + FlowsActions.openFlowDetailsEditor(dispatch); + } } else { Object.keys(artifacts).forEach(artifact => results.push({ @@ -102,13 +100,13 @@ const FlowsActions = Object.freeze({ serviceID, ...artifacts[artifact] })); - dispatch({type: actionTypes.FLOW_LIST_LOADED, results, participants, serviceID, diagramType}); + dispatch({type: actionTypes.FLOW_LIST_LOADED, results, participants, serviceID, diagramType, readonly}); } }, fetchArtifact(dispatch, {flow}){ let {serviceID, uniqueId, participants} = flow; - RestAPIUtil.fetch(baseUrl(serviceID, uniqueId)).then(response => { + return RestAPIUtil.fetch(baseUrl(serviceID, uniqueId)).then(response => { let {artifactName, base64Contents} = response; let {sequenceDiagramModel, ...other} = decodeContent(base64Contents); @@ -146,7 +144,7 @@ const FlowsActions = Object.freeze({ name: flow.artifactName }); } - createOrUpdate(flow).then(response => { + return createOrUpdate(flow).then(response => { let {uniqueId, artifactLabel} = response; flow = {...flow, uniqueId, artifactLabel}; if (isNew) { @@ -157,7 +155,7 @@ const FlowsActions = Object.freeze({ }, deleteFlow(dispatch, {flow}) { - RestAPIUtil.destroy(baseUrl(flow.serviceID, flow.uniqueId)).then(() => dispatch({ + return RestAPIUtil.destroy(baseUrl(flow.serviceID, flow.uniqueId)).then(() => dispatch({ type: actionTypes.DELETE_FLOW, flow })); @@ -179,10 +177,6 @@ const FlowsActions = Object.freeze({ dispatch({type: actionTypes.CLOSE_FLOW_DIAGRAM_EDITOR}); }, - flowDetailsDataChanged(dispatch, {deltaData}) { - dispatch({type: actionTypes.CURRENT_FLOW_DATA_CHANGED, deltaData}); - }, - reset(dispatch) { dispatch({type: actionTypes.RESET}); } diff --git a/openecomp-ui/src/sdc-app/flows/FlowsConstants.js b/openecomp-ui/src/sdc-app/flows/FlowsConstants.js index 5a43a4df4f..2b3d86bae2 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsConstants.js +++ b/openecomp-ui/src/sdc-app/flows/FlowsConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -46,3 +41,5 @@ export const enums = { INSTANTIATION_FLOWS: 'instantiationflows', MESSAGE_FLOWS: 'messageflows' }; + +export const FLOWS_EDITOR_FORM = 'FLOWS_FORM'; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js b/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js index eff1c36b80..f9585f985f 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js +++ b/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js @@ -1,42 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import FlowsEditorModalView from './FlowsEditorModalView.jsx'; import FlowsActions from './FlowsActions.js'; +import {FLOWS_EDITOR_FORM} from './FlowsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; export const mapStateToProps = ({flows}) => { - let {currentFlow = {artifactName: '', description: ''}, serviceID, diagramType, flowParticipants} = flows; - if(!currentFlow.serviceID){ - currentFlow.serviceID = serviceID; + let {data = {artifactName: '', description: ''}, serviceID, diagramType, flowParticipants, genericFieldInfo, formReady} = flows; + if(!data.serviceID){ + data.serviceID = serviceID; } - if(!currentFlow.artifactType){ - currentFlow.artifactType = diagramType; + if(!data.artifactType){ + data.artifactType = diagramType; } - if(!currentFlow.participants){ - currentFlow.participants = flowParticipants; + if(!data.participants){ + data.participants = flowParticipants; } + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); return { - currentFlow + currentFlow: data, + genericFieldInfo, + isFormValid, + formReady }; }; @@ -47,7 +48,8 @@ const mapActionsToProps = (dispatch, {isNewArtifact}) => { FlowsActions.createOrUpdateFlow(dispatch, {flow}, isNewArtifact); }, onCancel: () => FlowsActions.closeFlowDetailsEditor(dispatch), - onDataChanged: deltaData => FlowsActions.flowDetailsDataChanged(dispatch, {deltaData}) + onDataChanged: deltaData => ValidationHelper.dataChanged(dispatch, {deltaData, formName: FLOWS_EDITOR_FORM}), + onValidateForm: () => ValidationHelper.validateForm(dispatch, FLOWS_EDITOR_FORM) }; }; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx b/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx index 8441c7d1d6..1250a0b58e 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx +++ b/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx @@ -1,30 +1,54 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component} from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import Input from 'nfvo-components/input/validation/ValidationInput.jsx'; -import Form from 'nfvo-components/input/validation/ValidationForm.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; class FlowsEditorModalView extends Component { render() { - let {onCancel, onDataChanged, currentFlow} = this.props; + let {onCancel, onDataChanged, currentFlow, genericFieldInfo, formReady, isFormValid, onValidateForm} = this.props; let {artifactName, description} = currentFlow; return ( - <Form onSubmit={() => this.onSaveClicked()} onReset={onCancel}> + <div> + {genericFieldInfo && <Form + onSubmit={() => this.onSaveClicked()} + onReset={onCancel} formReady={formReady} isValid={isFormValid} onValidateForm={() => onValidateForm()} > <Input type='text' name='name' label={i18n('Name')} - validations={{required: true}} + isValid={genericFieldInfo['artifactName'].isValid} + errorText={genericFieldInfo['artifactName'].errorText} + isRequired={true} value={artifactName} onChange={artifactName => onDataChanged({artifactName})}/> <Input type='textarea' name='description' label={i18n('Description')} - validations={{required: true}} + isValid={genericFieldInfo['description'].isValid} + errorText={genericFieldInfo['description'].errorText} + isRequired={true} value={description} + overlayPos='bottom' onChange={description => onDataChanged({description})}/> - </Form> + </Form> } + </div> ); } diff --git a/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js b/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js index ff301b6e13..642c578eb7 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js +++ b/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js @@ -1,30 +1,28 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import FlowsActions from './FlowsActions.js'; import FlowsListEditorView from './FlowsListEditorView.jsx'; export const mapStateToProps = ({flows}) => { - let {flowList = [], isDisplayModal, isModalInEditMode, shouldShowWorkflowsEditor = true, currentFlow = undefined} = flows; - let isCheckedOut = currentFlow ? !currentFlow.readonly : true; + let {flowList = [], isDisplayModal, isModalInEditMode, shouldShowWorkflowsEditor = true, data = undefined, readonly} = flows; + let isCheckedOut = !readonly; + if(data && data.readonly){ + isCheckedOut = !data.readonly; + } return { flowList, @@ -32,7 +30,8 @@ export const mapStateToProps = ({flows}) => { isCheckedOut, isModalInEditMode, shouldShowWorkflowsEditor, - currentFlow + currentFlow: data, + readonly }; }; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx b/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx index 3cea3968ff..c5dbb4f909 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {PropTypes, Component} from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -18,6 +33,7 @@ class FlowsListEditorView extends Component { isModalInEditMode: PropTypes.bool, isCheckedOut: PropTypes.bool, shouldShowWorkflowsEditor: PropTypes.bool, + readonly: PropTypes.bool, onAddWorkflowClick: PropTypes.func, onEditFlowDetailsClick: PropTypes.func, @@ -55,7 +71,7 @@ class FlowsListEditorView extends Component { onAdd={onAddWorkflowClick} filterValue={localFilter} onFilter={filter => this.setState({localFilter: filter})} - isCheckedOut={isCheckedOut}> + isReadOnlyMode={!isCheckedOut}> {this.filterList().map(flow => this.renderWorkflowListItem(flow, isCheckedOut))} </ListEditorView> @@ -68,7 +84,7 @@ class FlowsListEditorView extends Component { renderWorkflowEditorModal() { let { isDisplayModal, isModalInEditMode} = this.props; return ( - <Modal show={isDisplayModal} animation={true} className='workflows-editor-modal'> + <Modal show={isDisplayModal} animation={true} className='onborading-modal workflows-editor-modal'> <Modal.Header> <Modal.Title> {`${isModalInEditMode ? i18n('Edit Workflow') : i18n('Create New Workflow')}`} diff --git a/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js b/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js index f025450a58..14bf595050 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js +++ b/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js @@ -1,24 +1,19 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './FlowsConstants.js'; +import {actionTypes, FLOWS_EDITOR_FORM} from './FlowsConstants.js'; export default (state = {}, action) => { switch (action.type) { @@ -28,7 +23,8 @@ export default (state = {}, action) => { flowList: action.results, flowParticipants: action.participants, serviceID: action.serviceID, - diagramType: action.diagramType + diagramType: action.diagramType, + readonly: action.readonly }; case actionTypes.ADD_OR_UPDATE_FLOW: case actionTypes.ARTIFACT_LOADED: @@ -49,14 +45,6 @@ export default (state = {}, action) => { serviceID: action.flow.serviceID, diagramType: action.flow.artifactType || state.diagramType }; - case actionTypes.CURRENT_FLOW_DATA_CHANGED: - return { - ...state, - currentFlow: { - ...state.currentFlow, - ...action.deltaData - } - }; case actionTypes.DELETE_FLOW: return { ...state, @@ -65,7 +53,21 @@ export default (state = {}, action) => { case actionTypes.OPEN_FLOW_DETAILS_EDITOR: return { ...state, - currentFlow: action.flow, + formName: FLOWS_EDITOR_FORM, + formReady: null, + genericFieldInfo: { + artifactName : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + description: { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + }, + data: action.flow, isDisplayModal: true, isModalInEditMode: Boolean(action.flow && action.flow.uniqueId) }; @@ -73,20 +75,20 @@ export default (state = {}, action) => { case actionTypes.CLOSE_FLOW_DETAILS_EDITOR: return { ...state, - currentFlow: undefined, + data: undefined, isDisplayModal: false, isModalInEditMode: false }; case actionTypes.OPEN_FLOW_DIAGRAM_EDITOR: return { ...state, - currentFlow: action.flow, + data: action.flow, shouldShowWorkflowsEditor: false }; case actionTypes.CLOSE_FLOW_DIAGRAM_EDITOR: return { ...state, - currentFlow: undefined, + data: undefined, shouldShowWorkflowsEditor: true }; case actionTypes.RESET: diff --git a/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx b/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx index 958f9a0a2d..873003492b 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import ReactDOM from 'react-dom'; import Configuration from 'sdc-app/config/Configuration.js'; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js b/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js index b3c0b2e27b..54043498f0 100644 --- a/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js +++ b/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js @@ -1,25 +1,22 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ import flowListReducer from './FlowsListReducer.js'; +import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; export default { - flows: flowListReducer + flows: createPlainDataReducer(flowListReducer) }; diff --git a/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx b/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx index c4ab41841b..d0a5bf3251 100644 --- a/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx +++ b/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component} from 'react'; import md5 from 'md5'; diff --git a/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx b/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx index 9970969884..b0bd40db40 100644 --- a/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx +++ b/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component, PropTypes} from 'react'; import Button from 'react-bootstrap/lib/Button.js'; import Sequencer from 'dox-sequence-diagram-ui'; @@ -8,7 +23,7 @@ class SequenceDiagram extends Component { static propTypes = { onSave: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, + onClose: PropTypes.func, model: PropTypes.object.isRequired }; diff --git a/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js b/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js index c2e10a6360..c83fdc91f0 100644 --- a/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js +++ b/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import emptyModel from './emptyModel.json'; function mergeLifelines(oldLifelines, newLifelines) { diff --git a/openecomp-ui/src/sdc-app/heatValidation.app.jsx b/openecomp-ui/src/sdc-app/heatValidation.app.jsx index eb58a79b25..6ff3c7375c 100644 --- a/openecomp-ui/src/sdc-app/heatValidation.app.jsx +++ b/openecomp-ui/src/sdc-app/heatValidation.app.jsx @@ -1,5 +1,19 @@ +/*! + * 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 '../../resources/scss/bootstrap.scss'; -import '../../resources/css/font-awesome.min.css'; import 'react-select/dist/react-select.min.css'; import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; import '../../resources/scss/style.scss'; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/Attachments.js b/openecomp-ui/src/sdc-app/heatvalidation/Attachments.js new file mode 100644 index 0000000000..d3e30b0df8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/Attachments.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 {connect} from 'react-redux'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import {mapStateToProps as attachmentsMapStateToProps, mapActionsToProps as attachmentsMapActionsToProps} from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js'; +// import AttachmentsView from './AttachmentsView.jsx'; +import AttachmentsView from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx'; +import UploadScreenActionHelper from './UploadScreenActionHelper.js'; +import HeatSetup from './HeatSetup'; + +export const mapStateToProps = (state) => { + let original = attachmentsMapStateToProps(state); + return { + ...original, + HeatSetupComponent: HeatSetup, + isReadOnlyMode: false + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + let original = attachmentsMapActionsToProps(dispatch, {softwareProductId}); + return { + ...original, + onDownload: heatCandidate => UploadScreenActionHelper.downloadHeatFile(dispatch, heatCandidate), + onUpload: formData => UploadScreenActionHelper.uploadFile(dispatch, formData), + onSave: (heatCandidate) => SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate}), + onProcessAndValidate: (heatData, heatDataCache) => UploadScreenActionHelper.processAndValidateHeat(dispatch, heatData, heatDataCache) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(AttachmentsView); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/HeatSetup.js b/openecomp-ui/src/sdc-app/heatvalidation/HeatSetup.js new file mode 100644 index 0000000000..925b0095ba --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/HeatSetup.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 {connect} from 'react-redux'; +import HeatSetupView from '../onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx'; +import UploadScreenActionHelper from './UploadScreenActionHelper.js'; +import {mapStateToProps, mapActionsToProps} from '../onboarding/softwareProduct/attachments/setup/HeatSetup.js'; + +const mapActionsToPropsExt = (dispatch) => { + return { + ...mapActionsToProps(dispatch,{}), + onProcessAndValidate: (heatData, heatDataCache) => UploadScreenActionHelper.processAndValidateHeat(dispatch, heatData, heatDataCache) + }; +}; + +export default connect(mapStateToProps, mapActionsToPropsExt, null, {withRef: true})(HeatSetupView); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx index 0bb496fc51..2d99c0f497 100644 --- a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx +++ b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx @@ -1,182 +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 React from 'react'; -import {connect} from 'react-redux'; -import Button from 'react-bootstrap/lib/Button.js'; -import Dropzone from 'react-dropzone'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import ProgressBar from 'nfvo-components/progressBar/ProgressBar.jsx'; -import Modal from 'nfvo-components/modal/Modal.jsx'; -import UploadScreenActionHelper from './UploadScreenActionHelper.js'; -import Attachments from './attachments/Attachments.js'; +import SoftwareProductAttachmentsView from './Attachments.js'; -const mapStateToProps = ({uploadScreen}) => { - let {upload} = uploadScreen; - return {uploadScreen: upload}; -}; - - -const mapActionsToProps = dispatch => { - return { - onUpload: (formData) => UploadScreenActionHelper.uploadFile(dispatch, formData), - openMainScreen: () => UploadScreenActionHelper.openMainScreen(dispatch) - }; -}; class UploadScreen extends React.Component { - state = { - complete: '10', - showModal: false, - fileName: '', - dragging: false, - files: [] - }; - - interval = ''; - render() { - let {uploadScreen} = this.props; - let {showAttachments} = uploadScreen; return( <div className='heat-validation-stand-alone'> - {showAttachments ? this.renderTree() : this.renderUploadScreen()} - </div> - ); - } - - renderUploadModal() { - let {complete, showModal, fileName} = this.state; - return ( - <Modal show={showModal} animation={true}> - <Modal.Header> - <Modal.Title>{i18n('Uploading attachments')}</Modal.Title> - </Modal.Header> - <Modal.Body> - <div className='upload-modal-body-content'> - <div> - <span className='title'>{i18n('File:')}</span> - <span className='file-name'>{fileName}</span> - </div> - <ProgressBar now={complete} label={`${complete}%`}/> - <div>{i18n('Upload in progress')}</div> - </div> - <Modal.Footer> - <Button bsStyle='primary' onClick={() => this.onRunBackground()}> - {i18n('Run in Background')} - </Button> - <Button bsStyle='primary' onClick={() => this.onCancel()}>{i18n('Cancel')}</Button> - </Modal.Footer> - </Modal.Body> - </Modal> - ); - } - - renderUploadScreen() { - return( - <div className='upload-screen'> - <div className='row'> - <div className='title'> - <h1>HEAT VALIDATION APPLICATION</h1> - </div> - </div> - <div className='row'> - <div className='col-md-2 col-md-offset-5'> - <Dropzone - className={`upload-screen-drop-zone ${this.state.dragging ? 'active-dragging' : ''}`} - onDrop={files => this.handleImportSubmit(files)} - onDragEnter={() => this.setState({dragging:true})} - onDragLeave={() => this.setState({dragging:false})} - multiple={false} - disableClick={true} - ref='fileInput' - name='fileInput' - accept='.zip'> - <div - className='upload-screen-upload-block'> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> - </Dropzone> - </div> - {this.renderUploadModal()} - </div> - </div> - ); - } - - renderTree() { - let {openMainScreen} = this.props; - return( - <div className='attachments-screen'> - <Attachments/> - <div className='back-button'> - <div className='upload-btn primary-btn' onClick={() => openMainScreen()}> - <span className='primary-btn-text'>{i18n('Back')}</span> - </div> - </div> + <SoftwareProductAttachmentsView /> </div> ); } - - handleImportSubmit(files) { - this.setState({ - showModal: true, - fileName: files[0].name, - dragging: false, - complete: '0', - files - }); - - - this.interval = setInterval(() => { - if (this.state.complete >= 90) { - clearInterval(this.interval); - this.setState({ - showModal: false, - fileName: '' - }); - this.startUploading(files); - } else { - this.setState({ - complete: (parseInt(this.state.complete) + 10).toString() - }); - } - }, 20); - - } - - onRunBackground() { - let {files} = this.state; - clearInterval(this.interval); - this.startUploading(files); - this.setState({showModal: false, files: []}); - } - - onCancel() { - clearInterval(this.interval); - this.setState({ - showModal: false, - fileName: '', - files: [] - }); - - } - - startUploading(files) { - let {onUpload} = this.props; - if (!(files && files.length)) { - return; - } - let file = files[0]; - let formData = new FormData(); - formData.append('upload', file); - this.refs.fileInput.value = ''; - onUpload(formData); - } - } -export default connect(mapStateToProps, mapActionsToProps)(UploadScreen); +export default UploadScreen; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js index 3b8de0f0d4..4e9120956e 100644 --- a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js +++ b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js @@ -1,60 +1,204 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; -import {actionTypes} from './UploadScreenConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import isEqual from 'lodash/isEqual.js'; +import cloneDeep from 'lodash/cloneDeep.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import {actionTypes as softwareProductsActionTypes} from '../onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes as HeatSetupActions} from '../onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js'; + + + +const options = { + headers: { + HTTP_CSP_ATTUID: 'validationOnlyVspUser' + } +}; + + +function getTimestampString() { + let date = new Date(); + let z = n => n < 10 ? '0' + n : n; + return `${date.getFullYear()}-${z(date.getMonth())}-${z(date.getDate())}_${z(date.getHours())}-${z(date.getMinutes())}`; +} + +function fetchVspId() { + + let vspId = sessionStorage.getItem('validationAppVspId'); + if (vspId) { + return Promise.resolve({value: vspId}); + }else { + return RestAPIUtil.fetch('/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/validation-vsp', options) + .then(response => { + sessionStorage.setItem('validationAppVspId', response.value); + return Promise.resolve(response); + }); + } + +} + + +function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) { + let filename; + let contentDisposition = xhr.getResponseHeader('content-disposition'); + let match = contentDisposition ? contentDisposition.match(/filename=(.*?)(;|$)/) : false; + if (match) { + filename = match[1]; + } else { + filename = defaultFilename; + } + + if (addTimestamp) { + filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`); + } + + let link = document.createElement('a'); + let url = URL.createObjectURL(blob); + link.href = url; + link.download = filename; + link.style.display = 'none'; + document.body.appendChild(link); + link.click(); + setTimeout(function(){ + document.body.removeChild(link); + URL.revokeObjectURL(url); + }, 0); +} + function uploadFile(formData) { - return RestAPIUtil.create('/sdc1/feProxy/onboarding-api/v1.0/validation/HEAT/validate', formData); + return fetchVspId() + .then(response => { + return RestAPIUtil.post(`/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/${response.value}/versions/0.1/orchestration-template-candidate`, formData, options); + }); +} + +function loadSoftwareProductHeatCandidate(dispatch){ + return fetchVspId() + .then(response => { + return RestAPIUtil.fetch(`/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/${response.value}/versions/0.1/orchestration-template-candidate/manifest`, options) + .then(response => dispatch({ + type: HeatSetupActions.MANIFEST_LOADED, + response + })); + }); +} + +function updateHeatCandidate(dispatch, heatCandidate) { + return fetchVspId() + .then(response => { + return RestAPIUtil.put(`/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/${response.value}/versions/0.1/orchestration-template-candidate/manifest`, + heatCandidate.heatData, options) + .then(null, error => { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + title: i18n('Save Failed'), + modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE, + modalComponentProps: { + validationResponse: error.responseJSON + }, + cancelButtonText: i18n('Ok') + } + }); + return Promise.reject(error); + }); + }); +} + +function fetchSoftwareProduct() { + return fetchVspId() + .then(response => { + return RestAPIUtil.fetch(`/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/${response.value}/versions/0.1`, options); + }); +} + +function downloadHeatFile() { + return fetchVspId() + .then(response => { + RestAPIUtil.fetch(`/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/${response.value}/versions/0.1/orchestration-template-candidate`, { + ...options, + dataType: 'binary' + }) + .done((blob, statusText, xhr) => showFileSaveDialog({ + blob, + xhr, + defaultFilename: 'HEAT_file.zip', + addTimestamp: true + })); + }); +} + +function processAndValidateHeatCandidate(dispatch) { + return fetchVspId() + .then(response => { + return RestAPIUtil.put(`/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/${response.value}/versions/0.1/orchestration-template-candidate/process`, {}, options) + .then(response => { + if (response.status === 'Success') { + fetchSoftwareProduct().then(response => { + dispatch({ + type: softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + }); + } + }); + }); } const UploadScreenActionHelper = { uploadFile(dispatch, formData) { - - Promise.resolve() + return Promise.resolve() .then(() => uploadFile(formData)) .then(response => { dispatch({ type: softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED, response }); - dispatch({ - type: actionTypes.OPEN_UPLOAD_SCREEN + type: HeatSetupActions.FILL_HEAT_SETUP_CACHE, + payload:{} }); + loadSoftwareProductHeatCandidate(dispatch); }) .catch(error => { - dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {title: 'File Upload Failed', msg: error.responseJSON.message} + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + title: i18n('File Upload Failed'), + msg: error.responseJSON.message, + cancelButtonText: i18n('Ok') + } }); }); }, - openMainScreen(dispatch) { - dispatch({ - type: actionTypes.OPEN_MAIN_SCREEN - }); - } + + processAndValidateHeat(dispatch, heatData, heatDataCache){ + return isEqual(heatData, heatDataCache) ? Promise.resolve() : + updateHeatCandidate(dispatch, heatData) + .then(() => processAndValidateHeatCandidate(dispatch)) + .then(() => dispatch({type: HeatSetupActions.FILL_HEAT_SETUP_CACHE, payload: cloneDeep(heatData)})); + }, + + downloadHeatFile(){ + return downloadHeatFile(); + }, }; export default UploadScreenActionHelper; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenConstants.js b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenConstants.js deleted file mode 100644 index 2766a975ec..0000000000 --- a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenConstants.js +++ /dev/null @@ -1,28 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import keyMirror from 'nfvo-utils/KeyMirror.js'; - -export const actionTypes = keyMirror({ - FILE_UPLOADED: null, - OPEN_UPLOAD_SCREEN: null, - OPEN_ATTACHMENTS_SCREEN: null, - OPEN_MAIN_SCREEN: null -}); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenReducer.js b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenReducer.js deleted file mode 100644 index e73e028233..0000000000 --- a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenReducer.js +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import {actionTypes} from './UploadScreenConstants.js'; - - -export default (state = {}, action) => { - switch (action.type) { - case actionTypes.OPEN_UPLOAD_SCREEN: - return {...state, showAttachments: true}; - case actionTypes.OPEN_MAIN_SCREEN: - return {...state, showAttachments: false}; - default: - return state; - } -}; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js deleted file mode 100644 index 2a6a992844..0000000000 --- a/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import {connect} from 'react-redux'; -import AttachmentsView from './AttachmentsView.jsx'; -import AttachmentsActionHelper from './AttachmentsActionHelper.js'; - - -const mapStateToProps = ({uploadScreen: {attachments}}) => { - let {attachmentsTree = false, hoveredNode, selectedNode, errorList} = attachments; - return { - attachmentsTree, - hoveredNode, - selectedNode, - errorList - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - - toggleExpanded: (path) => AttachmentsActionHelper.toggleExpanded(dispatch, {path}), - onSelectNode: (nodeName) => AttachmentsActionHelper.onSelectNode(dispatch, {nodeName}), - onUnselectNode: () => AttachmentsActionHelper.onUnselectNode(dispatch), - - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(AttachmentsView); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx deleted file mode 100644 index 7e2dda8d47..0000000000 --- a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx +++ /dev/null @@ -1,190 +0,0 @@ -import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import classNames from 'classnames'; -import Collapse from 'react-bootstrap/lib/Collapse.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; -import {nodeTypes, mouseActions} from './AttachmentsConstants'; - -const typeToIcon = Object.freeze({ - heat: 'building-o', - volume: 'database', - network: 'cloud', - artifact: 'gear', - env: 'server', - other: 'cube' -}); - -const leftPanelWidth = 250; - -class SoftwareProductAttachmentsView extends React.Component { - - static propTypes = { - attachmentsTree: React.PropTypes.object.isRequired - }; - state = { - treeWidth: '400', - }; - - render() { - let {attachmentsTree, errorList = []} = this.props; - - let {treeWidth} = this.state; - return ( - <div className='software-product-attachments'> - <div className='software-product-view'> - <div className='software-product-landing-view-right-side'> - <div className='software-product-attachments-main'> - <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}> - <div className='tree-wrapper'> - { - attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind])) - } - </div> - </div> - <div className='software-product-attachments-separator' onMouseDown={e => this.onChangeTreeWidth(e)} /> - <div className='software-product-attachments-error-list'> - {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ? - i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>} - </div> - </div> - </div> - </div> - </div> - ); - } - - - - renderNode(node, path) { - let isFolder = node.children && node.children.length > 0; - let {onSelectNode} = this.props; - return ( - <div key={node.name} className='tree-block-inside'> - { - <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}> - { - isFolder && - <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}> - <FontAwesome name='caret-down'/> - </div> - } - { - - <span className='tree-node-icon'> - <FontAwesome name={typeToIcon[node.type]}/> - </span> - } - { - - <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}> - {node.name} - </span> - } - </div> - } - { - isFolder && - <Collapse in={node.expanded}> - <div className='tree-node-children'> - { - node.children.map((child, ind) => this.renderNode(child, [...path, ind])) - } - </div> - </Collapse> - } - </div> - ); - } - - createErrorList(errorList, node, parent) { - if (node.errors) { - node.errors.forEach(error => errorList.push({ - error, - name: node.name, - parentName: parent.name, - type: node.type - })); - } - if (node.children && node.children.length) { - node.children.map((child) => this.createErrorList(errorList, child, node)); - } - } - - renderErrorList(errors) { - let prevError = {}; - let {selectedNode} = this.props; - return errors.map(error => { - let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName; - prevError = error; - - return ( - <div - key={error.name + error.errorMessage + error.parentName} - - onClick={() => this.selectNode(error.name)} - className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}> - <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}> - { - error.hasParent ? - i18n('{type} {name} in {parentName}: ', { - type: nodeTypes[error.type], - name: error.name, - parentName: error.parentName - }) : - i18n('{type} {name}: ', { - type: nodeTypes[error.type], - name: error.name - }) - } - </span> - <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span> - </div> - ); - }); - } - - selectNode(currentSelectedNode) { - let {onUnselectNode, onSelectNode, selectedNode} = this.props; - if (currentSelectedNode !== selectedNode) { - onSelectNode(currentSelectedNode); - }else{ - onUnselectNode(); - } - - } - - getTreeRowClassName(name) { - let {hoveredNode, selectedNode} = this.props; - return classNames({ - 'tree-node-row': true, - 'tree-node-selected': name === hoveredNode, - 'tree-node-clicked': name === selectedNode - }); - } - - getTreeTextClassName(node) { - let {selectedNode} = this.props; - return classNames({ - 'tree-element-text': true, - 'error-status': node.errors, - 'error-status-selected': node.name === selectedNode - }); - } - - onChangeTreeWidth(e) { - if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { - let onMouseMove = (e) => { - this.setState({treeWidth: e.clientX - leftPanelWidth}); - }; - let onMouseUp = () => { - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onMouseUp); - }; - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - } - } -} - -export default SoftwareProductAttachmentsView; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js index d39b2affd3..4945d33b23 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import LicenseModelActionHelper from './licenseModel/LicenseModelActionHelper.js'; import LicenseAgreementActionHelper from './licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; import FeatureGroupsActionHelper from './licenseModel/featureGroups/FeatureGroupsActionHelper.js'; @@ -29,9 +24,12 @@ import SoftwareProductNetworksActionHelper from './softwareProduct/networks/Soft 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 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 store from 'sdc-app/AppStore.js'; function setCurrentScreen(dispatch, screen, props = {}) { @@ -39,7 +37,8 @@ function setCurrentScreen(dispatch, screen, props = {}) { type: actionTypes.SET_CURRENT_SCREEN, currentScreen: { screen, - props + props, + forceBreadCrumbsUpdate: true } }); } @@ -48,21 +47,61 @@ function getCurrentLicenseModelVersion(licenseModelId) { return store.getState().licenseModelList.find(({id}) => id === licenseModelId).version; } +function getCurrentSoftwareProductVersion(softwareProductId) { + return store.getState().softwareProductList.find(({id}) => id === softwareProductId).version; +} + export default { navigateToOnboardingCatalog(dispatch) { LicenseModelActionHelper.fetchLicenseModels(dispatch); + LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); SoftwareProductActionHelper.fetchSoftwareProductList(dispatch); + SoftwareProductActionHelper.fetchFinalizedSoftwareProductList(dispatch); + OnboardActionHelper.resetOnboardStore(dispatch); setCurrentScreen(dispatch, enums.SCREEN.ONBOARDING_CATALOG); }, + autoSaveBeforeNavigate(dispatch, {softwareProductId, version, vspComponentId, dataToSave}) { + if(softwareProductId) { + if(vspComponentId) { + return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, { + softwareProductId, version, vspComponentId, + componentData: dataToSave.componentData, + qdata: dataToSave.qdata + }); + } + return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, { + softwareProduct: dataToSave.softwareProduct, + qdata: dataToSave.qdata + }); + } + return Promise.resolve(); + }, + + navigateToLicenseModelOverview(dispatch, {licenseModelId, version}) { + if (!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + + /** + * TODO change to specific rest + */ + + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + LicenseModelActionHelper.fetchLicenseModelItems(dispatch, {licenseModelId, version}).then(() =>{ + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_MODEL_OVERVIEW, {licenseModelId, version}); + }); + + }); + }, navigateToLicenseAgreements(dispatch, {licenseModelId, version}) { if(!version) { version = getCurrentLicenseModelVersion(licenseModelId); } LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version}); LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { - setCurrentScreen(dispatch, enums.SCREEN.LICENSE_AGREEMENTS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_AGREEMENTS, {licenseModelId, version}); }); }, @@ -71,7 +110,7 @@ export default { version = getCurrentLicenseModelVersion(licenseModelId); } FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); - setCurrentScreen(dispatch, enums.SCREEN.FEATURE_GROUPS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.FEATURE_GROUPS, {licenseModelId, version}); }, navigateToEntitlementPools(dispatch, {licenseModelId, version}) { @@ -79,7 +118,7 @@ export default { version = getCurrentLicenseModelVersion(licenseModelId); } EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); - setCurrentScreen(dispatch, enums.SCREEN.ENTITLEMENT_POOLS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.ENTITLEMENT_POOLS, {licenseModelId, version}); }, navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}) { @@ -87,13 +126,30 @@ export default { version = getCurrentLicenseModelVersion(licenseModelId); } LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); - setCurrentScreen(dispatch, enums.SCREEN.LICENSE_KEY_GROUPS, {licenseModelId}); + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_KEY_GROUPS, {licenseModelId, version}); + }, + + navigateToLicenseModelActivityLog(dispatch, {licenseModelId, version}){ + if(!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: licenseModelId, versionId: version.id}); + setCurrentScreen(dispatch, enums.SCREEN.ACTIVITY_LOG, {licenseModelId, version}); }, navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version, licensingVersion}) { + + if (!version) { + version = getCurrentSoftwareProductVersion(softwareProductId); + } + + SoftwareProductComponentsActionHelper.clearComponentsStore(dispatch); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}).then(response => { if(!licensingVersion) { licensingVersion = response[0].licensingVersion; + if (!licensingVersion) { + licensingVersion = {id: '1.0', label: '1.0'}; + } } if (!licenseModelId) { licenseModelId = response[0].vendorId; @@ -101,74 +157,84 @@ export default { SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}); SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); - + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version}); }); }, - navigateToSoftwareProductDetails(dispatch, {softwareProductId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DETAILS, {softwareProductId}); + navigateToSoftwareProductDetails(dispatch, {softwareProductId, version}) { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DETAILS, {softwareProductId, version}); }, - navigateToSoftwareProductAttachments(dispatch, {softwareProductId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS, {softwareProductId}); + navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}) { + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS, {softwareProductId, version}); }, navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}) { if (softwareProductId) { SoftwareProductProcessesActionHelper.fetchProcessesList(dispatch, {softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES, {softwareProductId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES, {softwareProductId, version}); }, navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version}) { if (softwareProductId) { SoftwareProductNetworksActionHelper.fetchNetworksList(dispatch, {softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS, {softwareProductId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS, {softwareProductId, version}); + }, + + navigateToSoftwareProductDependencies(dispatch, {softwareProductId, version}) { + SoftwareProductDependenciesActionHelper.fetchDependencies(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES, {softwareProductId, version}); + }, + + navigateToSoftwareProductComponents(dispatch, {softwareProductId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId, version}); }, - navigateToSoftwareProductComponents(dispatch, {softwareProductId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId}); + navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version}){ + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: softwareProductId, versionId: version.id}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG, {softwareProductId, version}); }, navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId, version}) { if (componentId && softwareProductId) { SoftwareProductComponentProcessesActionHelper.fetchProcessesList(dispatch, {componentId, softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, {softwareProductId, componentId, version}); }, - navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}){ - if (componentId && softwareProductId) { - SoftwareProductComponentsMonitoringAction.fetchExistingFiles(dispatch, {componentId, softwareProductId}); + navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, version, componentId}){ + if (componentId && softwareProductId && version) { + SoftwareProductComponentsMonitoringAction.fetchExistingFiles(dispatch, {componentId, softwareProductId, version}); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING, {softwareProductId, componentId, version}); }, - navigateToComponentStorage(dispatch, {softwareProductId, componentId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE, {softwareProductId, componentId}); + navigateToComponentStorage(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE, {softwareProductId, version, componentId}); }, - navigateToComponentCompute(dispatch, {softwareProductId, componentId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, componentId}); + navigateToComponentCompute(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, version, componentId}); }, navigateToComponentNetwork(dispatch, {softwareProductId, componentId, version}) { SoftwareProductComponentsNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK, {softwareProductId, version, componentId}); }, navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version}) { if (componentId && softwareProductId) { SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(dispatch, { - softwareProductId, - vspComponentId: componentId, - version - }); } - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL, {softwareProductId, componentId}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL, {softwareProductId, version, componentId}); }, navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version}) { @@ -182,7 +248,9 @@ export default { }); }, - navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}) { - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, componentId}); + navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, version, componentId}); } + }; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js deleted file mode 100644 index 4772c8d9af..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import {connect} from 'react-redux'; -import {default as OnboardingCatalogView, catalogItemTypes} from './OnboardingCatalogView.jsx'; -import OnboardingActionHelper from './OnboardingActionHelper.js'; -import LicenseModelCreationActionHelper from './licenseModel/creation/LicenseModelCreationActionHelper.js'; -import SoftwareProductCreationActionHelper from './softwareProduct/creation/SoftwareProductCreationActionHelper.js'; - - -const mapStateToProps = ({licenseModelList, softwareProductList, softwareProduct: {softwareProductCreation}, licenseModel: {licenseModelCreation}}) => { - - let modalToShow; - - if(licenseModelCreation.data) { - modalToShow = catalogItemTypes.LICENSE_MODEL; - } else if(softwareProductCreation.showModal && softwareProductCreation.data) { - modalToShow = catalogItemTypes.SOFTWARE_PRODUCT; - } - - return { - licenseModelList, - softwareProductList, - modalToShow - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onSelectLicenseModel({id: licenseModelId}) { - OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId}); - }, - onSelectSoftwareProduct(softwareProduct) { - let {id: softwareProductId, vendorId: licenseModelId, licensingVersion} = softwareProduct; - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion}); - }, - onAddSoftwareProductClick: () => SoftwareProductCreationActionHelper.open(dispatch), - onAddLicenseModelClick: () => LicenseModelCreationActionHelper.open(dispatch) - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(OnboardingCatalogView); diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx deleted file mode 100644 index f2a9db1342..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx +++ /dev/null @@ -1,147 +0,0 @@ -import React from 'react'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Modal from 'nfvo-components/modal/Modal.jsx'; -import objectValues from 'lodash/values.js'; -import LicenseModelCreation from './licenseModel/creation/LicenseModelCreation.js'; -import SoftwareProductCreation from './softwareProduct/creation/SoftwareProductCreation.js'; -import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; -import classnames from 'classnames'; -import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; - -export const catalogItemTypes = Object.freeze({ - LICENSE_MODEL: 'license-model', - SOFTWARE_PRODUCT: 'software-product' -}); - -const catalogItemTypeClasses = { - LICENSE_MODEL: 'license-model-type', - SOFTWARE_PRODUCT: 'software-product-type' -}; - -class OnboardingCatalogView extends React.Component { - - constructor(props) { - super(props); - this.state = {searchValue: ''}; - this.handleSearch = this.handleSearch.bind(this); - } - - handleSearch(event){ - this.setState({searchValue: event.target.value}); - } - - static propTypes = { - licenseModelList: React.PropTypes.array, - softwareProductList: React.PropTypes.array, - modalToShow: React.PropTypes.oneOf(objectValues(catalogItemTypes)), - onSelectLicenseModel: React.PropTypes.func.isRequired, - onSelectSoftwareProduct: React.PropTypes.func.isRequired, - onAddLicenseModelClick: React.PropTypes.func.isRequired, - onAddSoftwareProductClick: React.PropTypes.func.isRequired - }; - - getModalDetails() { - const {modalToShow} = this.props; - switch (modalToShow) { - case catalogItemTypes.LICENSE_MODEL: - return { - title: i18n('New License Model'), - element: <LicenseModelCreation/> - }; - case catalogItemTypes.SOFTWARE_PRODUCT: - return { - title: i18n('New Software Product'), - element: <SoftwareProductCreation/> - }; - } - } - - render() { - const modalDetails = this.getModalDetails(); - const {licenseModelList, softwareProductList, onSelectLicenseModel, onSelectSoftwareProduct, onAddLicenseModelClick, onAddSoftwareProductClick, modalToShow} = this.props; - - return ( - <div className='catalog-view'> - <div className='catalog-header'> - <div className='catalog-header-title'>{i18n('Onboarding Catalog')}</div> - <ExpandableInput - onChange={this.handleSearch} - iconType='search'/> - </div> - <div className='catalog-list'> - - <div className='create-catalog-item tile'> - <div className='plus-section'> - <div className='plus-icon-button'/> - <span>{i18n('ADD')}</span> - </div> - <div className='primary-btn new-license-model'> - <span - className='primary-btn-text' - onClick={() => onAddLicenseModelClick()}>{i18n('New License Model')}</span></div> - <div className='primary-btn'> - <span - className='primary-btn-text' - onClick={() => onAddSoftwareProductClick()}>{i18n('New Vendor Software Product')}</span> - </div> - </div> - {licenseModelList.filter(vlm => vlm.vendorName.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1).map(licenseModel => this.renderTile( - { - ...licenseModel, - name: licenseModel.vendorName - }, - catalogItemTypeClasses.LICENSE_MODEL, - () => onSelectLicenseModel(licenseModel)) - )} - {softwareProductList.filter(vsp => vsp.name.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1).map(softwareProduct => this.renderTile(softwareProduct, - catalogItemTypeClasses.SOFTWARE_PRODUCT, - () => onSelectSoftwareProduct(softwareProduct)) - )} - </div> - <Modal - show={Boolean(modalDetails)} - className={`${this.getCatalogItemTypeClassByItemType(modalToShow)}-modal`}> - <Modal.Header> - <Modal.Title>{modalDetails && modalDetails.title}</Modal.Title> - </Modal.Header> - <Modal.Body> - { - modalDetails && modalDetails.element - } - </Modal.Body> - </Modal> - </div> - ); - - } - - getCatalogItemTypeClassByItemType(catalogItemType) { - switch (catalogItemType) { - case catalogItemTypes.LICENSE_MODEL: - return catalogItemTypeClasses.LICENSE_MODEL; - case catalogItemTypes.SOFTWARE_PRODUCT: - return catalogItemTypeClasses.SOFTWARE_PRODUCT; - } - } - - renderTile(catalogItemData, catalogItemTypeClass, onSelect) { - let {status: itemStatus} = VersionControllerUtils.getCheckOutStatusKindByUserID(catalogItemData.status, catalogItemData.lockingUser); - return ( - <div className='catalog-tile tile' key={catalogItemData.id} onClick={() => onSelect()}> - <div className={`catalog-tile-type ${catalogItemTypeClass}`}/> - <div className='catalog-tile-icon'> - <div className={`icon ${catalogItemTypeClass}-icon`}></div> - </div> - <div className='catalog-tile-content'> - <div className='catalog-tile-item-details'> - <div className='catalog-tile-item-name'>{catalogItemData.name}</div> - <div className='catalog-tile-item-version'>V {catalogItemData.version}</div> - </div> - <div className={classnames('catalog-tile-check-in-status', {'sprite-new checkout-editable-status-icon': itemStatus === 'Locked'})}> - </div> - </div> - </div> - ); - } -} -export default OnboardingCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js index d9c177f606..7811950073 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -29,16 +24,21 @@ export const enums = keyMirror({ BREADCRUMS: { LICENSE_MODEL: 'LICENSE_MODEL', + LICENSE_MODEL_OVERVIEW: 'LICENSE_MODEL_OVERVIEW', LICENSE_AGREEMENTS: 'LICENSE_AGREEMENTS', FEATURE_GROUPS: 'FEATURE_GROUPS', ENTITLEMENT_POOLS: 'ENTITLEMENT_POOLS', LICENSE_KEY_GROUPS: 'LICENSE_KEY_GROUPS', + ACTIVITY_LOG: 'ACTIVITY_LOG', SOFTWARE_PRODUCT: 'SOFTWARE_PRODUCT', + SOFTWARE_PRODUCT_LANDING_PAGE: 'SOFTWARE_PRODUCT_LANDING_PAGE', SOFTWARE_PRODUCT_DETAILS: 'SOFTWARE_PRODUCT_DETAILS', SOFTWARE_PRODUCT_ATTACHMENTS: 'SOFTWARE_PRODUCT_ATTACHMENTS', SOFTWARE_PRODUCT_PROCESSES: 'SOFTWARE_PRODUCT_PROCESSES', SOFTWARE_PRODUCT_NETWORKS: 'SOFTWARE_PRODUCT_NETWORKS', + SOFTWARE_PRODUCT_DEPENDENCIES: 'SOFTWARE_PRODUCT_DEPENDENCIES', + SOFTWARE_PRODUCT_ACTIVITY_LOG: 'SOFTWARE_PRODUCT_ACTIVITY_LOG', SOFTWARE_PRODUCT_COMPONENTS: 'SOFTWARE_PRODUCT_COMPONENTS', SOFTWARE_PRODUCT_COMPONENT_PROCESSES: 'SOFTWARE_PRODUCT_COMPONENT_PROCESSES', SOFTWARE_PRODUCT_COMPONENT_STORAGE: 'SOFTWARE_PRODUCT_COMPONENT_STORAGE', @@ -50,16 +50,20 @@ export const enums = keyMirror({ SCREEN: { ONBOARDING_CATALOG: null, + LICENSE_MODEL_OVERVIEW: null, LICENSE_AGREEMENTS: null, FEATURE_GROUPS: null, ENTITLEMENT_POOLS: null, LICENSE_KEY_GROUPS: null, + ACTIVITY_LOG: null, SOFTWARE_PRODUCT_LANDING_PAGE: null, SOFTWARE_PRODUCT_DETAILS: null, SOFTWARE_PRODUCT_ATTACHMENTS: null, SOFTWARE_PRODUCT_PROCESSES: null, SOFTWARE_PRODUCT_NETWORKS: null, + SOFTWARE_PRODUCT_DEPENDENCIES: null, + SOFTWARE_PRODUCT_ACTIVITY_LOG: null, SOFTWARE_PRODUCT_COMPONENTS: null, SOFTWARE_PRODUCT_COMPONENT_PROCESSES: null, SOFTWARE_PRODUCT_COMPONENT_COMPUTE: null, diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx index c4627b11b3..e8a844b03f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import ReactDOM from 'react-dom'; import {connect} from 'react-redux'; @@ -9,8 +24,12 @@ import Application from 'sdc-app/Application.jsx'; import store from 'sdc-app/AppStore.js'; import Configuration from 'sdc-app/config/Configuration.js'; -import OnboardingCatalog from './OnboardingCatalog.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 {doesHeatDataExist} from './softwareProduct/attachments/SoftwareProductAttachmentsUtils.js'; + import LicenseAgreementListEditor from './licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; import FeatureGroupListEditor from './licenseModel/featureGroups/FeatureGroupListEditor.js'; import LicenseKeyGroupsListEditor from './licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js'; @@ -21,6 +40,7 @@ import SoftwareProductDetails from './softwareProduct/details/SoftwareProductDe import SoftwareProductAttachments from './softwareProduct/attachments/SoftwareProductAttachments.js'; import SoftwareProductProcesses from './softwareProduct/processes/SoftwareProductProcesses.js'; import SoftwareProductNetworks from './softwareProduct/networks/SoftwareProductNetworks.js'; +import SoftwareProductDependencies from './softwareProduct/dependencies/SoftwareProductDependencies.js'; import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponentsList.js'; import SoftwareProductComponentProcessesList from './softwareProduct/components/processes/SoftwareProductComponentProcessesList.js'; import SoftwareProductComponentStorage from './softwareProduct/components/storage/SoftwareProductComponentStorage.js'; @@ -30,8 +50,12 @@ import SoftwareProductComponentsCompute from './softwareProduct/components/compu import SoftwareProductComponentLoadBalancing from './softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js'; import SoftwareProductComponentsMonitoring from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js'; import {navigationItems as SoftwareProductNavigationItems, 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'; + +import HeatSetupActionHelper from './softwareProduct/attachments/setup/HeatSetupActionHelper.js'; -import {enums} from './OnboardingConstants.js'; +import {actionTypes, enums} from './OnboardingConstants.js'; import OnboardingActionHelper from './OnboardingActionHelper.js'; @@ -64,17 +88,21 @@ class OnboardingView extends React.Component { {(() => { switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: - return <OnboardingCatalog {...props}/>; + return <Onboard {...props}/>; case enums.SCREEN.LICENSE_AGREEMENTS: case enums.SCREEN.FEATURE_GROUPS: case enums.SCREEN.ENTITLEMENT_POOLS: case enums.SCREEN.LICENSE_KEY_GROUPS: + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + case enums.SCREEN.ACTIVITY_LOG: return ( <LicenseModel currentScreen={currentScreen}> { (()=>{ switch(screen) { + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + return <LicenseModelOverview {...props}/>; case enums.SCREEN.LICENSE_AGREEMENTS: return <LicenseAgreementListEditor {...props}/>; case enums.SCREEN.FEATURE_GROUPS: @@ -83,6 +111,8 @@ class OnboardingView extends React.Component { return <EntitlementPoolsListEditor {...props}/>; case enums.SCREEN.LICENSE_KEY_GROUPS: return <LicenseKeyGroupsListEditor {...props}/>; + case enums.SCREEN.ACTIVITY_LOG: + return <ActivityLog {...props}/>; } })() } @@ -94,6 +124,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: @@ -102,6 +133,7 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: return ( <SoftwareProduct currentScreen={currentScreen}> { @@ -112,11 +144,13 @@ class OnboardingView extends React.Component { case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: return <SoftwareProductDetails {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: - return <SoftwareProductAttachments className='no-padding-content-area' {...props} />; + return <SoftwareProductAttachments className='no-padding-content-area' {...props} />; case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: return <SoftwareProductProcesses {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: return <SoftwareProductNetworks {...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + return <SoftwareProductDependencies {...props} />; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: return <SoftwareProductComponentsList {...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: @@ -133,6 +167,8 @@ class OnboardingView extends React.Component { return <SoftwareProductComponentLoadBalancing{...props}/>; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: return <SoftwareProductComponentsMonitoring {...props}/>; + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: + return <ActivityLog {...props}/>; } })() } @@ -184,151 +220,195 @@ export default class OnboardingPunchOut { handleData(data) { let {breadcrumbs: {selectedKeys = []} = {}} = data; let dispatch = action => store.dispatch(action); - let {softwareProductList, softwareProduct: {softwareProductEditor: {data: {id: currentSoftwareProductId, version: currentSoftwareProductVersion} = {}}}, - licenseModelList, licenseModel: {licenseModelEditor: {data: {id: currentLicenseModelId, version: currentLicenseModelVersion} = {}}}} = store.getState(); - + 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; if (this.programmaticBreadcrumbsUpdate) { this.prevSelectedKeys = selectedKeys; this.programmaticBreadcrumbsUpdate = false; return; } - if (!isEqual(selectedKeys, this.prevSelectedKeys)) { this.breadcrumbsPrefixSelected = isEqual(selectedKeys, this.prevSelectedKeys && this.prevSelectedKeys.slice(0, selectedKeys.length)); - this.prevSelectedKeys = selectedKeys; - if (selectedKeys.length === 0) { - OnboardingActionHelper.navigateToOnboardingCatalog(dispatch); - } else if (selectedKeys.length === 1 || selectedKeys[1] === enums.BREADCRUMS.LICENSE_MODEL) { - let [licenseModelId, , licenseModelScreen] = selectedKeys; - if (!licenseModelScreen) { - licenseModelScreen = enums.BREADCRUMS.LICENSE_AGREEMENTS; - } - if(currentLicenseModelId !== licenseModelId) { - currentLicenseModelVersion = licenseModelList.find(lm => lm.id === licenseModelId).version; - } - switch (licenseModelScreen) { - case enums.BREADCRUMS.LICENSE_AGREEMENTS: - OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version: currentLicenseModelVersion}); - break; - case enums.BREADCRUMS.FEATURE_GROUPS: - OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion}); - break; - case enums.BREADCRUMS.ENTITLEMENT_POOLS: - OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version: currentLicenseModelVersion}); - break; - case enums.BREADCRUMS.LICENSE_KEY_GROUPS: - OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion}); - break; - } - } else if (selectedKeys.length <= 4 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT) { - let [licenseModelId, , softwareProductId, softwareProductScreen] = selectedKeys; - let softwareProduct = softwareProductId ? - softwareProductList.find(({id}) => id === softwareProductId) : - softwareProductList.find(({vendorId}) => vendorId === licenseModelId); - if (!softwareProductId) { - softwareProductId = softwareProduct.id; - } - if(currentSoftwareProductId !== softwareProductId) { - currentSoftwareProductVersion = softwareProduct.version; - } - switch (softwareProductScreen) { - case enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS: - OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: - OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES: - OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS: - OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: - OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); - dispatch({ - type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM, - mapOfExpandedIds: { - [SoftwareProductNavigationItems.COMPONENTS]: true - } - }); - break; - default: - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); - break; - } - }else if (selectedKeys.length === 5 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) { - let [licenseModelId, , softwareProductId, , componentId] = selectedKeys; - let softwareProduct = softwareProductId ? - softwareProductList.find(({id}) => id === softwareProductId) : - softwareProductList.find(({vendorId}) => vendorId === licenseModelId); - if (!softwareProductId) { - softwareProductId = softwareProduct.id; - } - if(currentSoftwareProductId !== softwareProductId) { - currentSoftwareProductVersion = softwareProduct.version; - } - OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); - }else if (selectedKeys.length === 6 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) { - let [licenseModelId, , softwareProductId, , componentId, componentScreen] = selectedKeys; - let softwareProduct = softwareProductId ? - softwareProductList.find(({id}) => id === softwareProductId) : - softwareProductList.find(({vendorId}) => vendorId === licenseModelId); - if (!softwareProductId) { - softwareProductId = softwareProduct.id; - } - if(currentSoftwareProductId !== softwareProductId) { - currentSoftwareProductVersion = softwareProduct.version; - } - switch (componentScreen) { - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL: - OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: - OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: - OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK: - OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE: - OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: - OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId}); - break; - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING: - OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}); - break; - } - } else { - console.error('Unknown breadcrumbs path: ', selectedKeys); + const [, screenType, prevVspId, , prevComponentId] = this.prevSelectedKeys || []; + 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, { + softwareProductId: prevVspId, + version: currentSoftwareProductVersion, + vspComponentId: prevComponentId, + dataToSave + }); } + + let {currentScreen: {props: {softwareProductId}}, softwareProduct: {softwareProductAttachments: {heatSetup, heatSetupCache}}} = store.getState(); + let heatSetupPopupPromise = currentScreen.screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS ? + HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : + Promise.resolve(); + Promise.all([preNavigate, heatSetupPopupPromise]).then(() => { + this.prevSelectedKeys = selectedKeys; + if (selectedKeys.length === 0) { + OnboardingActionHelper.navigateToOnboardingCatalog(dispatch); + } else if (selectedKeys.length === 1 || selectedKeys[1] === enums.BREADCRUMS.LICENSE_MODEL) { + let [licenseModelId, , licenseModelScreen] = selectedKeys; + if (!licenseModelScreen) { + licenseModelScreen = enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW; + } + if (currentLicenseModelId !== licenseModelId) { + currentLicenseModelVersion = licenseModelList.find(lm => lm.id === licenseModelId).version; + } + switch (licenseModelScreen) { + case enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW: + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.LICENSE_AGREEMENTS: + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.FEATURE_GROUPS: + OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.ENTITLEMENT_POOLS: + OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.LICENSE_KEY_GROUPS: + OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.ACTIVITY_LOG: + OnboardingActionHelper.navigateToLicenseModelActivityLog(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + } + } else if (selectedKeys.length <= 4 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT) { + let [licenseModelId, , softwareProductId, softwareProductScreen] = selectedKeys; + let softwareProduct = softwareProductId ? + softwareProductList.find(({id}) => id === softwareProductId) : + softwareProductList.find(({vendorId}) => vendorId === licenseModelId); + if (!softwareProductId) { + softwareProductId = softwareProduct.id; + } + if (currentSoftwareProductId !== softwareProductId) { + currentSoftwareProductVersion = softwareProduct.version; + } + switch (softwareProductScreen) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_LANDING_PAGE: + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES: + OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS: + OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES: + OnboardingActionHelper.navigateToSoftwareProductDependencies(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ACTIVITY_LOG: + OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + dispatch({ + type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM, + mapOfExpandedIds: { + [SoftwareProductNavigationItems.COMPONENTS]: true + } + }); + break; + default: + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + } + } else if (selectedKeys.length === 5 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) { + let [licenseModelId, , softwareProductId, , componentId] = selectedKeys; + let softwareProduct = softwareProductId ? + softwareProductList.find(({id}) => id === softwareProductId) : + softwareProductList.find(({vendorId}) => vendorId === licenseModelId); + if (!softwareProductId) { + softwareProductId = softwareProduct.id; + } + if (currentSoftwareProductId !== softwareProductId) { + currentSoftwareProductVersion = softwareProduct.version; + } + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); + } else if (selectedKeys.length === 6 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) { + let [licenseModelId, , softwareProductId, , componentId, componentScreen] = selectedKeys; + let softwareProduct = softwareProductId ? + softwareProductList.find(({id}) => id === softwareProductId) : + softwareProductList.find(({vendorId}) => vendorId === licenseModelId); + if (!softwareProductId) { + softwareProductId = softwareProduct.id; + } + if (currentSoftwareProductId !== softwareProductId) { + currentSoftwareProductVersion = softwareProduct.version; + } + switch (componentScreen) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}); + break; + } + } else { + console.error('Unknown breadcrumbs path: ', selectedKeys); + } + }).catch(() => { + store.dispatch({ + type: actionTypes.SET_CURRENT_SCREEN, + currentScreen: { + ...currentScreen, + forceBreadCrumbsUpdate: true + } + }); + }); } } handleStoreChange() { - let {currentScreen, licenseModelList, softwareProductList} = store.getState(); - let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList}; - - if (!isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { + let {currentScreen, licenseModelList, softwareProductList, + softwareProduct: {softwareProductComponents: {componentsList}, softwareProductAttachments: {heatSetup}}} = store.getState(); + let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup}; + if (currentScreen.forceBreadCrumbsUpdate || !isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { this.prevBreadcrumbsData = breadcrumbsData; this.breadcrumbsPrefixSelected = false; this.programmaticBreadcrumbsUpdate = true; - let breadcrumbs = this.buildBreadcrumbs(breadcrumbsData); this.onEvent('breadcrumbsupdated', breadcrumbs); + store.dispatch({ + type: actionTypes.SET_CURRENT_SCREEN, + currentScreen: { + ...currentScreen, + forceBreadCrumbsUpdate: false + } + }); } } - buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList}) { - let componentsList; - if(props.componentId) { - componentsList = store.getState().softwareProduct.softwareProductComponents.componentsList; - } + buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList, componentsList, heatSetup}) { let screenToBreadcrumb; switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: @@ -338,11 +418,15 @@ export default class OnboardingPunchOut { case enums.SCREEN.FEATURE_GROUPS: case enums.SCREEN.ENTITLEMENT_POOLS: case enums.SCREEN.LICENSE_KEY_GROUPS: + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + case enums.SCREEN.ACTIVITY_LOG: screenToBreadcrumb = { [enums.SCREEN.LICENSE_AGREEMENTS]: enums.BREADCRUMS.LICENSE_AGREEMENTS, [enums.SCREEN.FEATURE_GROUPS]: enums.BREADCRUMS.FEATURE_GROUPS, [enums.SCREEN.ENTITLEMENT_POOLS]: enums.BREADCRUMS.ENTITLEMENT_POOLS, - [enums.SCREEN.LICENSE_KEY_GROUPS]: enums.BREADCRUMS.LICENSE_KEY_GROUPS + [enums.SCREEN.LICENSE_KEY_GROUPS]: enums.BREADCRUMS.LICENSE_KEY_GROUPS, + [enums.SCREEN.LICENSE_MODEL_OVERVIEW]: enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW, + [enums.SCREEN.ACTIVITY_LOG]: enums.BREADCRUMS.ACTIVITY_LOG }; return [ { @@ -365,6 +449,9 @@ export default class OnboardingPunchOut { }, { selectedKey: screenToBreadcrumb[screen], menuItems: [{ + key: enums.BREADCRUMS.LICENSE_MODEL_OVERVIEW, + displayText: i18n('Overview') + },{ key: enums.BREADCRUMS.LICENSE_AGREEMENTS, displayText: i18n('License Agreements') }, { @@ -376,6 +463,9 @@ export default class OnboardingPunchOut { }, { key: enums.BREADCRUMS.LICENSE_KEY_GROUPS, displayText: i18n('License Key Groups') + }, { + key: enums.BREADCRUMS.ACTIVITY_LOG, + displayText: i18n('Activity Log') }] } ]; @@ -385,6 +475,8 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: @@ -395,11 +487,14 @@ export default class OnboardingPunchOut { case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: 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_NETWORKS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS + [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG]: enums.BREADCRUMS.SOFTWARE_PRODUCT_ACTIVITY_LOG }; let componentScreenToBreadcrumb = { [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, @@ -438,24 +533,46 @@ export default class OnboardingPunchOut { displayText: name })) }, - ...(screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] : [{ + ...(/*screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] :*/ [{ selectedKey: screenToBreadcrumb[screen] || enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, menuItems: [{ + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_LANDING_PAGE, + displayText: i18n('Overview') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, displayText: i18n('General') }, { - key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, - displayText: i18n('Attachments') - }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, displayText: i18n('Process Details') }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS, displayText: i18n('Networks') }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES, + displayText: i18n('Components Dependencies') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, + displayText: i18n('Attachments') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ACTIVITY_LOG, + displayText: i18n('Activity Log') + }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, displayText: i18n('Components') - }] + }].filter(item => { + let isHeatData = doesHeatDataExist(heatSetup); + let isComponentsData = componentsList.length > 0; + switch (item.key) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: + return isHeatData; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: + return isComponentsData; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DEPENDENCIES: + return isComponentsData; + default: + return true; + } + }) }]) ]; if(props.componentId) { diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js index 9af2427243..46fc58db95 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js @@ -1,26 +1,21 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes, enums} from './OnboardingConstants.js'; -export const currentScreenReducer = (state = {screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => { +export const currentScreenReducer = (state = {forceBreadCrumbsUpdate: false, screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => { if (action.type === actionTypes.SET_CURRENT_SCREEN) { return action.currentScreen; } diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js index 92d53a3d4f..9428dd5829 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js @@ -1,36 +1,34 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {currentScreenReducer} from './OnboardingReducers.js'; import licenseModelListReducer from './licenseModel/LicenseModelListReducer.js'; import finalizedLicenseModelListReducer from './licenseModel/FinalizedLicenseModelListReducer.js'; import licenseModelReducer from './licenseModel/LicenseModelReducer.js'; import softwareProductReducer from './softwareProduct/SoftwareProductReducer.js'; import softwareProductListReducer from './softwareProduct/SoftwareProductListReducer.js'; - +import finalizedSoftwareProductReducer from './softwareProduct/FinalizedSoftwareProductReducer.js'; +import onboardReducer from './onboard/OnboardReducer.js'; export default { currentScreen: currentScreenReducer, licenseModelList: licenseModelListReducer, finalizedLicenseModelList: finalizedLicenseModelListReducer, + finalizedSoftwareProductList: finalizedSoftwareProductReducer, licenseModel: licenseModelReducer, softwareProduct: softwareProductReducer, - softwareProductList: softwareProductListReducer + softwareProductList: softwareProductListReducer, + onboard: onboardReducer }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js index a851e77dc8..cc9d9c536d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './LicenseModelConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js index ad91a0da65..e21b0a81b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js @@ -1,30 +1,24 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; - +import ActivityLogActionHelper from 'nfvo-components/activity-log/ActivityLogActionHelper.js'; import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; @@ -45,6 +39,11 @@ const buildNavigationBarProps = (licenseModel, screen) => { name: vendorName, items: [ { + id: navigationItems.LICENSE_MODEL_OVERVIEW, + name: i18n('Overview'), + meta + }, + { id: navigationItems.LICENSE_AGREEMENTS, name: i18n('License Agreements'), meta @@ -63,15 +62,22 @@ const buildNavigationBarProps = (licenseModel, screen) => { id: navigationItems.LICENSE_KEY_GROUPS, name: i18n('License Key Groups'), meta + }, + { + id: navigationItems.ACTIVITY_LOG, + name: i18n('Activity Log'), + meta } ] }]; const activeItemId = ({ + [enums.SCREEN.LICENSE_MODEL_OVERVIEW]: navigationItems.LICENSE_MODEL_OVERVIEW, [enums.SCREEN.LICENSE_AGREEMENTS]: navigationItems.LICENSE_AGREEMENTS, [enums.SCREEN.FEATURE_GROUPS]: navigationItems.FEATURE_GROUPS, [enums.SCREEN.ENTITLEMENT_POOLS]: navigationItems.ENTITLEMENT_POOLS, - [enums.SCREEN.LICENSE_KEY_GROUPS]: navigationItems.LICENSE_KEY_GROUPS + [enums.SCREEN.LICENSE_KEY_GROUPS]: navigationItems.LICENSE_KEY_GROUPS, + [enums.SCREEN.ACTIVITY_LOG]: navigationItems.ACTIVITY_LOG })[screen]; return { @@ -82,9 +88,7 @@ const buildNavigationBarProps = (licenseModel, screen) => { const buildVersionControllerProps = (licenseModel) => { let {version, viewableVersions, status: currentStatus, lockingUser} = licenseModel; - let {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ? - VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) : - {status: currentStatus, isCheckedOut: false}; + let {status, isCheckedOut} = VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser); return { version, @@ -104,29 +108,46 @@ const mapStateToProps = ({licenseModel: {licenseModelEditor}}, {currentScreen: { const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {licenseModelId}}}) => { + return { - onVersionControllerAction: action => - LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => { + onVersionControllerAction: (action, version) => + LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action, version}).then((newVersion) => { switch(screen) { + case enums.SCREEN.LICENSE_MODEL_OVERVIEW: + /** + * TODO change to specific rest + */ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: newVersion}); + break; case enums.SCREEN.LICENSE_AGREEMENTS: - LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId}); + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: newVersion}); break; case enums.SCREEN.FEATURE_GROUPS: - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}); + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: newVersion}); break; case enums.SCREEN.ENTITLEMENT_POOLS: - EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId}); + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version: newVersion}); break; case enums.SCREEN.LICENSE_KEY_GROUPS: - LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version: newVersion}); + break; + case enums.SCREEN.ACTIVITY_LOG: + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: licenseModelId, versionId: newVersion.id}); break; } }), - onVersionSwitching: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}), - onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch), + onVersionSwitching: version => { + LicenseModelActionHelper.switchVersion(dispatch, {licenseModelId, version}); + if(screen === enums.SCREEN.ACTIVITY_LOG) { + ActivityLogActionHelper.fetchActivityLog(dispatch, {itemId: licenseModelId, versionId: version.id}); + } + }, onNavigate: ({id, meta: {version}}) => { switch(id) { + case navigationItems.LICENSE_MODEL_OVERVIEW: + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version}); + break; case navigationItems.LICENSE_AGREEMENTS: OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version}); break; @@ -139,6 +160,9 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {licenseMod case navigationItems.LICENSE_KEY_GROUPS: OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}); break; + case navigationItems.ACTIVITY_LOG: + OnboardingActionHelper.navigateToLicenseModelActivityLog(dispatch, {licenseModelId, version}); + break; } } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js index a379a2c40f..186f1cbc7b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js @@ -1,29 +1,28 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes} from './LicenseModelConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; import {actionsEnum as vcActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import LicenseAgreementActionHelper from './licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupsActionHelper from './featureGroups/FeatureGroupsActionHelper.js'; +import EntitlementPoolsActionHelper from './entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseKeyGroupsActionHelper from './licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -39,12 +38,32 @@ function fetchFinalizedLicenseModels() { } function fetchLicenseModelById(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl()}${licenseModelId}${versionQuery}`); + const {id: versionId} = version; + return RestAPIUtil.fetch(`${baseUrl()}${licenseModelId}/versions/${versionId}`); +} + +function putLicenseModelAction(id, action, version) { + const {id: versionId} = version; + return RestAPIUtil.put(`${baseUrl()}${id}/versions/${versionId}/actions`, {action: action}); +} + +function putLicenseModel(licenseModel) { + let {id, vendorName, description, iconRef, version: {id: versionId}} = licenseModel; + return RestAPIUtil.put(`${baseUrl()}${id}/versions/${versionId}`, { + vendorName, + description, + iconRef + }); +} + +function adjustMinorVersion(version, value) { + let ar = version.split('.'); + return ar[0] + '.' + (parseInt(ar[1]) + value); } -function putLicenseModelAction(id, action) { - return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +function adjustMajorVersion(version, value) { + let ar = version.split('.'); + return (parseInt(ar[0]) + value) + '.0'; } const LicenseModelActionHelper = { @@ -67,13 +86,11 @@ const LicenseModelActionHelper = { }, fetchLicenseModelById(dispatch, {licenseModelId, version}) { - return fetchLicenseModelById(licenseModelId, version).then(response => { - if(version) { - response.version = version; - } + + return fetchLicenseModelById(licenseModelId, version).then(response => { dispatch({ type: actionTypes.LICENSE_MODEL_LOADED, - response + response: {...response, version} }); }); }, @@ -85,17 +102,68 @@ const LicenseModelActionHelper = { }); }, - performVCAction(dispatch, {licenseModelId, action}) { - return putLicenseModelAction(licenseModelId, action).then(() => { + fetchLicenseModelItems(dispatch, {licenseModelId, version}) { + return Promise.all([ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version}), + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}), + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}), + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}) + ]); + }, + + performVCAction(dispatch, {licenseModelId, action, version}) { + return putLicenseModelAction(licenseModelId, action, version).then(() => { if(action === vcActionsEnum.SUBMIT){ dispatch({ - type: NotificationConstants.NOTIFY_SUCCESS, - data: {title: i18n('Submit Succeeded'), msg: i18n('This license model successfully submitted'), timeout: 2000} + type: modalActionTypes.GLOBAL_MODAL_SUCCESS, + data: { + title: i18n('Submit Succeeded'), + msg: i18n('This license model successfully submitted'), + cancelButtonText: i18n('OK'), + timeout: 2000 + } }); } - return LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId}); + + let newVersionId = version.id; + /* + TODO Temorary switch to change version label + */ + switch(action) { + case vcActionsEnum.CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, 1); + break; + case vcActionsEnum.UNDO_CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, -1); + break; + case vcActionsEnum.SUBMIT: + newVersionId = adjustMajorVersion(version.label, 1); + } + + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version:{id: newVersionId, label: newVersionId}}); + return Promise.resolve({id: newVersionId, label: newVersionId}); + }); + }, + + switchVersion(dispatch, {licenseModelId, version}) { + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version: {id: version.id, label: version.label}}).then(() => { + LicenseModelActionHelper.fetchLicenseModelItems(dispatch, {licenseModelId, version}); + }); + }, + + saveLicenseModel(dispatch, {licenseModel}) { + return putLicenseModel(licenseModel).then(() => { + dispatch({ + type: actionTypes.ADD_LICENSE_MODEL, + licenseModel + }); + dispatch({ + type: actionTypes.LICENSE_MODEL_LOADED, + response: licenseModel + }); }); } + }; export default LicenseModelActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js index 13fa9f5284..4ba10c3a68 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -29,8 +24,10 @@ export const actionTypes = keyMirror({ }); export const navigationItems = keyMirror({ - LICENSE_AGREEMENTS: 'License Agreements', - FEATURE_GROUPS: 'Feature Groups', - ENTITLEMENT_POOLS: 'Entitlement Pools', - LICENSE_KEY_GROUPS: 'License Key Groups' + LICENSE_MODEL_OVERVIEW: 'overview', + LICENSE_AGREEMENTS: 'license-agreements', + FEATURE_GROUPS: 'feature-groups', + ENTITLEMENT_POOLS: 'entitlement-pools', + LICENSE_KEY_GROUPS: 'license-key-groups', + ACTIVITY_LOG: 'activity-log' }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js index e92e32aa9e..add5ac6a98 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js @@ -1,28 +1,23 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './LicenseModelConstants.js'; export default (state = {}, action) => { switch (action.type) { - case actionTypes.LICENSE_MODEL_LOADED: + case actionTypes.LICENSE_MODEL_LOADED: return { ...state, data: action.response diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js index 8874c4ce21..fd73b462a3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './LicenseModelConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js index 5982b9f8ab..9a2d114bdc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js @@ -1,66 +1,85 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {combineReducers} from 'redux'; +import activityLogReducer from 'nfvo-components/activity-log/ActivityLogReducer.js'; + import licenseModelCreationReducer from './creation/LicenseModelCreationReducer.js'; import licenseModelEditorReducer from './LicenseModelEditorReducer.js'; import licenseAgreementListReducer from './licenseAgreement/LicenseAgreementListReducer.js'; import licenseAgreementEditorReducer from './licenseAgreement/LicenseAgreementEditorReducer.js'; -import {actionTypes as licenseAgreementActionTypes} from './licenseAgreement/LicenseAgreementConstants.js'; import featureGroupsEditorReducer from './featureGroups/FeatureGroupsEditorReducer.js'; import featureGroupsListReducer from './featureGroups/FeatureGroupsListReducer.js'; -import {actionTypes as featureGroupsActionConstants} from './featureGroups/FeatureGroupsConstants'; import entitlementPoolsListReducer from './entitlementPools/EntitlementPoolsListReducer.js'; import entitlementPoolsEditorReducer from './entitlementPools/EntitlementPoolsEditorReducer.js'; -import {actionTypes as entitlementPoolsConstants} from './entitlementPools/EntitlementPoolsConstants'; import licenseKeyGroupsEditorReducer from './licenseKeyGroups/LicenseKeyGroupsEditorReducer.js'; import licenseKeyGroupsListReducer from './licenseKeyGroups/LicenseKeyGroupsListReducer.js'; -import {actionTypes as licenseKeyGroupsConstants} from './licenseKeyGroups/LicenseKeyGroupsConstants.js'; + +import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; + +import {actionTypes as licenseModelOverviewConstants, selectedButton, VLM_DESCRIPTION_FORM} from './overview/LicenseModelOverviewConstants.js'; export default combineReducers({ - licenseModelCreation: licenseModelCreationReducer, + licenseModelCreation: createPlainDataReducer(licenseModelCreationReducer), licenseModelEditor: licenseModelEditorReducer, licenseAgreement: combineReducers({ - licenseAgreementEditor: licenseAgreementEditorReducer, - licenseAgreementList: licenseAgreementListReducer, - licenseAgreementToDelete: (state = false, action) => action.type === licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM ? action.licenseAgreementToDelete : state + licenseAgreementEditor: createPlainDataReducer(licenseAgreementEditorReducer), + licenseAgreementList: licenseAgreementListReducer }), featureGroup: combineReducers({ - featureGroupEditor: featureGroupsEditorReducer, - featureGroupsList: featureGroupsListReducer, - featureGroupToDelete: (state = false, action) => action.type === featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM ? action.featureGroupToDelete : state + featureGroupEditor: createPlainDataReducer(featureGroupsEditorReducer), + featureGroupsList: featureGroupsListReducer }), entitlementPool: combineReducers({ - entitlementPoolEditor: entitlementPoolsEditorReducer, - entitlementPoolsList: entitlementPoolsListReducer, - entitlementPoolToDelete: (state = false, action) => action.type === entitlementPoolsConstants.ENTITLEMENT_POOLS_DELETE_CONFIRM ? action.entitlementPoolToDelete : state + entitlementPoolEditor: createPlainDataReducer(entitlementPoolsEditorReducer), + entitlementPoolsList: entitlementPoolsListReducer }), licenseKeyGroup: combineReducers({ - licenseKeyGroupsEditor: licenseKeyGroupsEditorReducer, - licenseKeyGroupsList: licenseKeyGroupsListReducer, - licenseKeyGroupToDelete: (state = false, action) => action.type === licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM ? action.licenseKeyGroupToDelete : state + licenseKeyGroupsEditor: createPlainDataReducer(licenseKeyGroupsEditorReducer), + licenseKeyGroupsList: licenseKeyGroupsListReducer }), + licenseModelOverview: combineReducers({ + selectedTab: (state = selectedButton.VLM_LIST_VIEW, action) => action.type === licenseModelOverviewConstants.LICENSE_MODEL_OVERVIEW_TAB_SELECTED ? action.buttonTab : state, + descriptionEditor: createPlainDataReducer(function(state = false, action) { + if (action.type === licenseModelOverviewConstants.LM_DATA_CHANGED) { + return { + ...state, + data : { + description : action.description + }, + formReady: null, + formName: VLM_DESCRIPTION_FORM, + genericFieldInfo: { + 'description': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + } + } + }; + //return action.description; + } else { + return state; + } + } + )}), + activityLog: activityLogReducer }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js index 63d0f27b6a..d85618c85f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js @@ -1,40 +1,50 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; import LicenseModelCreationActionHelper from './LicenseModelCreationActionHelper.js'; import LicenseModelCreationView from './LicenseModelCreationView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; + +export const mapStateToProps = ({licenseModelList, licenseModel: {licenseModelCreation}}) => { + let {genericFieldInfo} = licenseModelCreation; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); -const mapStateToProps = ({licenseModel: {licenseModelCreation}}) => licenseModelCreation; + let VLMNames = {}; + for (let i = 0; i < licenseModelList.length; i++) { + VLMNames[licenseModelList[i].vendorName] = licenseModelList[i].id; + } + + return {...licenseModelCreation, isFormValid: isFormValid, VLMNames}; +}; -const mapActionsToProps = (dispatch) => { +export const mapActionsToProps = (dispatch) => { return { - onDataChanged: deltaData => LicenseModelCreationActionHelper.dataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => LicenseModelCreationActionHelper.close(dispatch), onSubmit: (licenseModel) => { LicenseModelCreationActionHelper.close(dispatch); - LicenseModelCreationActionHelper.createLicenseModel(dispatch, {licenseModel}).then(licenseModelId => { - OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId}); + LicenseModelCreationActionHelper.createLicenseModel(dispatch, {licenseModel}).then(response => { + LicenseModelActionHelper.fetchLicenseModels(dispatch).then(() => { + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId: response.value}); + }); }); - } + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js index c2a0409bd2..51f5b7f276 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js @@ -1,27 +1,24 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; import {actionTypes} from './LicenseModelCreationConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -29,7 +26,7 @@ function baseUrl() { } function createLicenseModel(licenseModel) { - return RestAPIUtil.create(baseUrl(), { + return RestAPIUtil.post(baseUrl(), { vendorName: licenseModel.vendorName, description: licenseModel.description, iconRef: 'icon' @@ -43,30 +40,28 @@ export default { dispatch({ type: actionTypes.OPEN }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.LICENSE_MODEL_CREATION, + title: i18n('New License Model') + } + }); }, close(dispatch){ dispatch({ type: actionTypes.CLOSE }); - }, - dataChanged(dispatch, {deltaData}){ dispatch({ - type: actionTypes.DATA_CHANGED, - deltaData + type: modalActionTypes.GLOBAL_MODAL_CLOSE }); }, createLicenseModel(dispatch, {licenseModel}){ - return createLicenseModel(licenseModel).then(response => { - LicenseModelActionHelper.addLicenseModel(dispatch, { - licenseModel: { - ...licenseModel, - id: response.value - } - }); - return response.value; - }); + return createLicenseModel(licenseModel); } + }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js index 603d177048..28f1175676 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js @@ -1,27 +1,23 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ OPEN: null, - CLOSE: null, - DATA_CHANGED: null + CLOSE: null }); + +export const LICENSE_MODEL_CREATION_FORM_NAME = 'LMCREATIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js index a54d1b3089..879d356de2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js @@ -1,42 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './LicenseModelCreationConstants.js'; +import {actionTypes, LICENSE_MODEL_CREATION_FORM_NAME} from './LicenseModelCreationConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.OPEN: return { ...state, - data: {} + formReady: null, + formName: LICENSE_MODEL_CREATION_FORM_NAME, + data: {}, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + }, + 'vendorName' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}] + } + } }; case actionTypes.CLOSE: return {}; - case actionTypes.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx index 4dccc9e1c4..80040460c0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx @@ -1,7 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import Validator from 'nfvo-utils/Validator.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import {LICENSE_MODEL_CREATION_FORM_NAME} from './LicenseModelCreationConstants.js'; const LicenseModelPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -13,39 +30,49 @@ class LicenseModelCreationView extends React.Component { static propTypes = { data: LicenseModelPropType, + VLMNames: React.PropTypes.object, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, + onValidateForm: React.PropTypes.func.isRequired, onCancel: React.PropTypes.func.isRequired }; render() { - let {data = {}, onDataChanged} = this.props; + let {data = {}, onDataChanged, genericFieldInfo} = this.props; let {vendorName, description} = data; return ( <div> - <ValidationForm + {genericFieldInfo && <Form ref='validationForm' hasButtons={true} onSubmit={ () => this.submit() } onReset={ () => this.props.onCancel() } - labledButtons={true}> - <ValidationInput + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.validate() }> + <Input value={vendorName} label={i18n('Vendor Name')} - ref='vendor-name' - onChange={vendorName => onDataChanged({vendorName})} - validations={{maxLength: 25, required: true}} + data-test-id='vendor-name' + onChange={vendorName => onDataChanged({vendorName}, LICENSE_MODEL_CREATION_FORM_NAME, {vendorName: name => this.validateName(name)})} + isValid={genericFieldInfo.vendorName.isValid} + errorText={genericFieldInfo.vendorName.errorText} type='text' + isRequired={true} className='field-section'/> - <ValidationInput + <Input + isRequired={true} value={description} label={i18n('Description')} - ref='description' - onChange={description => onDataChanged({description})} - validations={{maxLength: 1000, required: true}} + data-test-id='vendor-description' + overlayPos='bottom' + onChange={description => onDataChanged({description}, LICENSE_MODEL_CREATION_FORM_NAME)} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} type='textarea' className='field-section'/> - </ValidationForm> + </Form>} </div> ); } @@ -55,6 +82,18 @@ class LicenseModelCreationView extends React.Component { const {data:licenseModel} = this.props; this.props.onSubmit(licenseModel); } + + validateName(value) { + const {data: {id}, VLMNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: VLMNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('License model by the name \'' + value + '\' already exists. License model name must be unique')}; + } + + validate() { + this.props.onValidateForm(LICENSE_MODEL_CREATION_FORM_NAME); + } } export default LicenseModelCreationView; 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 631597a5b0..fe95b034dd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js @@ -1,40 +1,35 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes as entitlementPoolsActionTypes } from './EntitlementPoolsConstants.js'; import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; -function baseUrl(licenseModelId) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/entitlement-pools`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/entitlement-pools`; } -function fetchEntitlementPoolsList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +function fetchEntitlementPoolsList(licenseModelId, version) { + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function postEntitlementPool(licenseModelId, entitlementPool) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function postEntitlementPool(licenseModelId, entitlementPool, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, @@ -49,8 +44,8 @@ function postEntitlementPool(licenseModelId, entitlementPool) { } -function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool) { - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${entitlementPool.id}`, { +function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool, version) { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${entitlementPool.id}`, { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, @@ -64,8 +59,8 @@ function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlement }); } -function deleteEntitlementPool(licenseModelId, entitlementPoolId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${entitlementPoolId}`); +function deleteEntitlementPool(licenseModelId, entitlementPoolId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}`); } @@ -84,8 +79,8 @@ export default { }); }, - deleteEntitlementPool(dispatch, {licenseModelId, entitlementPoolId}) { - return deleteEntitlementPool(licenseModelId, entitlementPoolId).then(() => { + deleteEntitlementPool(dispatch, {licenseModelId, entitlementPoolId, version}) { + return deleteEntitlementPool(licenseModelId, entitlementPoolId, version).then(() => { dispatch({ type: entitlementPoolsActionTypes.DELETE_ENTITLEMENT_POOL, entitlementPoolId @@ -106,9 +101,9 @@ export default { }); }, - saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}) { + saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool, version}) { if (previousEntitlementPool) { - return putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool).then(() => { + return putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool, version).then(() => { dispatch({ type: entitlementPoolsActionTypes.EDIT_ENTITLEMENT_POOL, entitlementPool @@ -116,11 +111,12 @@ export default { }); } else { - return postEntitlementPool(licenseModelId, entitlementPool).then(response => { + return postEntitlementPool(licenseModelId, entitlementPool, version).then(response => { dispatch({ type: entitlementPoolsActionTypes.ADD_ENTITLEMENT_POOL, entitlementPool: { ...entitlementPool, + referencingFeatureGroups: [], id: response.value } }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx deleted file mode 100644 index 04f038f5f0..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; - -function renderMsg(entitlementPoolToDelete) { - let poolName = entitlementPoolToDelete ? entitlementPoolToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{poolName}"?', {poolName}); - let subMsg = entitlementPoolToDelete - && entitlementPoolToDelete.referencingFeatureGroups - && entitlementPoolToDelete.referencingFeatureGroups.length > 0 ? - i18n('This entitlement pool is associated with one or more feature groups') : - ''; - return ( - <div> - <p>{msg}</p> - <p>{subMsg}</p> - </div> - ); -}; - -const mapStateToProps = ({licenseModel: {entitlementPool}}, {licenseModelId}) => { - let {entitlementPoolToDelete} = entitlementPool; - const show = entitlementPoolToDelete !== false; - return { - show, - title: 'Warning!', - type: 'warning', - msg: renderMsg(entitlementPoolToDelete), - confirmationDetails: {entitlementPoolToDelete, licenseModelId} - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onConfirmed: ({entitlementPoolToDelete, licenseModelId}) => { - EntitlementPoolsActionHelper.deleteEntitlementPool(dispatch, { - licenseModelId, - entitlementPoolId: entitlementPoolToDelete.id - }); - EntitlementPoolsActionHelper.hideDeleteConfirm(dispatch); - }, - onDeclined: () => { - EntitlementPoolsActionHelper.hideDeleteConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - 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 8a855076f3..ba0b238b17 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js @@ -1,25 +1,21 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; export const actionTypes = keyMirror({ @@ -27,7 +23,6 @@ export const actionTypes = keyMirror({ ADD_ENTITLEMENT_POOL: null, EDIT_ENTITLEMENT_POOL: null, DELETE_ENTITLEMENT_POOL: null, - ENTITLEMENT_POOLS_DELETE_CONFIRM: null, entitlementPoolsEditor: { OPEN: null, @@ -107,6 +102,14 @@ export const optionsInputValues = { ] }; +export const extractValue = (item) => { + if (item === undefined) {return '';} //TODO fix it later + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; +}; +export const extractUnits = (units) => { + if (units === undefined) {return '';} //TODO fix it later + return units === 'Absolute' ? '' : '%'; +}; - +export const SP_ENTITLEMENT_POOL_FORM = 'SPENTITLEMENTPOOL'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js index d5bd07e929..f89cf8fbb5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js @@ -1,52 +1,60 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; import EntitlementPoolsEditorView from './EntitlementPoolsEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({licenseModel: {entitlementPool}}) => { - let {data} = entitlementPool.entitlementPoolEditor; - - let previousData; + let {data, genericFieldInfo, formReady} = entitlementPool.entitlementPoolEditor; + + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + let previousData, EPNames = {}; const entitlementPoolId = data ? data.id : null; if(entitlementPoolId) { previousData = entitlementPool.entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId); } + const list = entitlementPool.entitlementPoolsList; + for (let i = 0; i < list.length; i++) { + EPNames[list[i].name] = list[i].id; + } + return { data, - previousData + genericFieldInfo, + previousData, + isFormValid, + formReady, + EPNames }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { - onDataChanged: deltaData => EntitlementPoolsActionHelper.entitlementPoolsEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch), onSubmit: ({previousEntitlementPool, entitlementPool}) => { EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch); - EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}); - } + EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool, version}); + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; 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 86e97ecf8d..db1a3a97ca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js @@ -1,30 +1,79 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes, defaultState} from './EntitlementPoolsConstants.js'; +import {actionTypes, defaultState, SP_ENTITLEMENT_POOL_FORM} from './EntitlementPoolsConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.entitlementPoolsEditor.OPEN: return { ...state, + formReady: null, + formName: SP_ENTITLEMENT_POOL_FORM, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + }, + 'manufacturerReferenceNumber' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 100}] + }, + 'increments' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 120}] + }, + 'operationalScope' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'thresholdUnits' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'thresholdValue' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'entitlementMetric' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'aggregationFunction' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'time' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + }, data: action.entitlementPool ? {...action.entitlementPool} : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA }; case actionTypes.entitlementPoolsEditor.DATA_CHANGED: 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 77c5a12e03..d484437015 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx @@ -1,14 +1,31 @@ +/*! + * 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 Validator from 'nfvo-utils/Validator.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType} from './EntitlementPoolsConstants.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +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 {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; - const EntitlementPoolPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, @@ -33,11 +50,172 @@ const EntitlementPoolPropType = React.PropTypes.shape({ }) }); +const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, thresholdValueValidation}) => { + let { + name, description, manufacturerReferenceNumber, operationalScope , aggregationFunction, thresholdUnits, thresholdValue, + increments, time, entitlementMetric} = data; + + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name}, SP_ENTITLEMENT_POOL_FORM, {name: validateName})} + isValid={genericFieldInfo.name.isValid} + isRequired={true} + errorText={genericFieldInfo.name.errorText} + label={i18n('Name')} + value={name} + data-test-id='create-ep-name' + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={true} + + isRequired={true} + onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {operationalScope: validateChoiceWithOther})} + onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], + other: operationalScope}}, SP_ENTITLEMENT_POOL_FORM, {operationalScope: validateChoiceWithOther})} + label={i18n('Operational Scope')} + data-test-id='create-ep-operational-scope' + type='select' + multiSelectedEnum={operationalScope && operationalScope.choices} + otherValue={operationalScope && operationalScope.other} + values={EntitlementPoolsOptionsInputValues.OPERATIONAL_SCOPE} + isValid={genericFieldInfo.operationalScope.isValid} + errorText={genericFieldInfo.operationalScope.errorText} /> + </GridItem> + <GridItem colSpan={2} stretch> + <Input + onChange={description => onDataChanged({description}, SP_ENTITLEMENT_POOL_FORM)} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + label={i18n('Description')} + value={description} + isRequired={true} + data-test-id='create-ep-description' + type='textarea'/> + </GridItem> + <GridItem colSpan={2}> + <div className='threshold-section'> + <Input + isRequired={true} + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({thresholdUnits: val}, SP_ENTITLEMENT_POOL_FORM); + // TODO make sure that the value is valid too + onDataChanged({thresholdValue: thresholdValue}, SP_ENTITLEMENT_POOL_FORM,{thresholdValue : thresholdValueValidation});} + + } + value={thresholdUnits} + label={i18n('Threshold Units')} + data-test-id='create-ep-threshold-units' + isValid={genericFieldInfo.thresholdUnits.isValid} + errorText={genericFieldInfo.thresholdUnits.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + {EntitlementPoolsOptionsInputValues.THRESHOLD_UNITS.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + + <Input + className='entitlement-pools-form-row-threshold-value' + onChange={thresholdValue => onDataChanged({thresholdValue}, SP_ENTITLEMENT_POOL_FORM, + {thresholdValue : thresholdValueValidation})} + label={i18n('Threshold Value')} + isValid={genericFieldInfo.thresholdValue.isValid} + errorText={genericFieldInfo.thresholdValue.errorText} + data-test-id='create-ep-threshold-value' + value={thresholdValue} + isRequired={true} + type='text'/> + </div> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: entitlementMetric, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {entitlementMetric: validateChoiceWithOther})} + onOtherChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: optionInputOther.OTHER, + other: entitlementMetric}}, SP_ENTITLEMENT_POOL_FORM, {entitlementMetric: validateChoiceWithOther})} + label={i18n('Entitlement Metric')} + data-test-id='create-ep-entitlement-metric' + type='select' + required={true} + selectedEnum={entitlementMetric && entitlementMetric.choice} + otherValue={entitlementMetric && entitlementMetric.other} + values={EntitlementPoolsOptionsInputValues.ENTITLEMENT_METRIC} + isValid={genericFieldInfo.entitlementMetric.isValid} + errorText={genericFieldInfo.entitlementMetric.errorText} /> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: aggregationFunction, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {aggregationFunction: validateChoiceWithOther})} + onOtherChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: optionInputOther.OTHER, + other: aggregationFunction}}, SP_ENTITLEMENT_POOL_FORM, {aggregationFunction: validateChoiceWithOther})} + label={i18n('Aggregate Function')} + data-test-id='create-ep-aggregate-function' + type='select' + required={true} + selectedEnum={aggregationFunction && aggregationFunction.choice} + otherValue={aggregationFunction && aggregationFunction.other} + values={EntitlementPoolsOptionsInputValues.AGGREGATE_FUNCTION} + isValid={genericFieldInfo.aggregationFunction.isValid} + errorText={genericFieldInfo.aggregationFunction.errorText} /> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber}, SP_ENTITLEMENT_POOL_FORM)} + label={i18n('Manufacturer Reference Number')} + value={manufacturerReferenceNumber} + isRequired={true} + data-test-id='create-ep-reference-number' + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={time => onDataChanged({time:{choice: time, other: ''}}, + SP_ENTITLEMENT_POOL_FORM, {time: validateChoiceWithOther})} + onOtherChange={time => onDataChanged({time:{choice: optionInputOther.OTHER, + other: time}}, SP_ENTITLEMENT_POOL_FORM, {time: validateTimeOtherValue})} + label={i18n('Time')} + data-test-id='create-ep-time' + type='select' + required={true} + selectedEnum={time && time.choice} + otherValue={time && time.other} + values={EntitlementPoolsOptionsInputValues.TIME} + isValid={genericFieldInfo.time.isValid} + errorText={genericFieldInfo.time.errorText} /> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={increments => onDataChanged({increments}, SP_ENTITLEMENT_POOL_FORM)} + label={i18n('Increments')} + value={increments} + data-test-id='create-ep-increments' + type='text'/> + </GridItem> + </GridSection> + ); +}; + class EntitlementPoolsEditorView extends React.Component { static propTypes = { data: EntitlementPoolPropType, previousData: EntitlementPoolPropType, + EPNames: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, @@ -49,119 +227,88 @@ class EntitlementPoolsEditorView extends React.Component { }; render() { - let {data = {}, onDataChanged, isReadOnlyMode} = this.props; - let { - name, description, manufacturerReferenceNumber, operationalScope, aggregationFunction, thresholdUnits, thresholdValue, - increments, time, entitlementMetric} = data; - let thresholdValueValidation = thresholdUnits === thresholdUnitType.PERCENTAGE ? {numeric: true, required: true, maxValue: 100} : {numeric: true, required: true}; - let timeValidation = time && time.choice === optionInputOther.OTHER ? {numeric: true, required: true} : {required: true}; + let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo} = this.props; + return ( - <ValidationForm - ref='validationForm' - hasButtons={true} - onSubmit={ () => this.submit() } - onReset={ () => this.props.onCancel() } - labledButtons={true} - isReadOnlyMode={isReadOnlyMode} - className='entitlement-pools-form'> - <div className='entitlement-pools-form-row'> - <ValidationInput - onChange={name => onDataChanged({name})} - label={i18n('Name')} - value={name} - validations={{maxLength: 120, required: true}} - type='text'/> + <div> + { + genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(SP_ENTITLEMENT_POOL_FORM) } + className='entitlement-pools-form'> + <EntitlementPoolsFormContent + data={data} + genericFieldInfo={genericFieldInfo} + onDataChanged={onDataChanged} + validateName={(value)=> this.validateName(value)} + validateTimeOtherValue ={(value)=> this.validateTimeOtherValue(value)} + validateChoiceWithOther={(value)=> this.validateChoiceWithOther(value)} + thresholdValueValidation={(value, state)=> this.thresholdValueValidation(value, state)}/> + </Form> + } + </div> + ); + } - <ValidationInput - isMultiSelect={true} - onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}})} - onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], other: operationalScope}})} - multiSelectedEnum={operationalScope && operationalScope.choices} - label={i18n('Operational Scope')} - otherValue={operationalScope && operationalScope.other} - validations={{required: true}} - values={EntitlementPoolsOptionsInputValues.OPERATIONAL_SCOPE}/> + submit() { + const {data: entitlementPool, previousData: previousEntitlementPool} = this.props; + this.props.onSubmit({entitlementPool, previousEntitlementPool}); + } - </div> - <div className='entitlement-pools-form-row'> - <ValidationInput - onChange={description => onDataChanged({description})} - label={i18n('Description')} - value={description} - validations={{maxLength: 1000, required: true}} - type='textarea'/> - <div className='entitlement-pools-form-row-group'> - <div className='entitlement-pools-form-row'> - <ValidationInput - onEnumChange={thresholdUnits => onDataChanged({thresholdUnits})} - selectedEnum={thresholdUnits} - label={i18n('Threshold Value')} - type='select' - values={EntitlementPoolsOptionsInputValues.THRESHOLD_UNITS} - validations={{required: true}}/> - <ValidationInput - className='entitlement-pools-form-row-threshold-value' - onChange={thresholdValue => onDataChanged({thresholdValue})} - value={thresholdValue} - validations={thresholdValueValidation} - type='text'/> - </div> - - <ValidationInput - onEnumChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: entitlementMetric, other: ''}})} - onOtherChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: optionInputOther.OTHER, other: entitlementMetric}})} - selectedEnum={entitlementMetric && entitlementMetric.choice} - otherValue={entitlementMetric && entitlementMetric.other} - label={i18n('Entitlement Metric')} - validations={{required: true}} - values={EntitlementPoolsOptionsInputValues.ENTITLEMENT_METRIC}/> - <ValidationInput - onEnumChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: aggregationFunction, other: ''}})} - onOtherChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: optionInputOther.OTHER, other: aggregationFunction}})} - selectedEnum={aggregationFunction && aggregationFunction.choice} - otherValue={aggregationFunction && aggregationFunction.other} - validations={{required: true}} - label={i18n('Aggregate Function')} - values={EntitlementPoolsOptionsInputValues.AGGREGATE_FUNCTION}/> - - </div> - </div> - <div className='entitlement-pools-form-row'> + validateName(value) { + const {data: {id}, EPNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: EPNames}); - <ValidationInput - onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber})} - label={i18n('Manufacturer Reference Number')} - value={manufacturerReferenceNumber} - validations={{maxLength: 100, required: true}} - type='text'/> + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Entitlement pool by the name \'' + value + '\' already exists. Entitlement pool name must be unique')}; + } - <ValidationInput - onEnumChange={time => onDataChanged({time:{choice: time, other: ''}})} - onOtherChange={time => onDataChanged({time:{choice: optionInputOther.OTHER, other: time}})} - selectedEnum={time && time.choice} - otherValue={time && time.other} - validations={timeValidation} - label={i18n('Time')} - values={EntitlementPoolsOptionsInputValues.TIME}/> - </div> - <div className='entitlement-pools-form-row'> - <ValidationInput - onChange={increments => onDataChanged({increments})} - label={i18n('Increments')} - value={increments} - validations={{maxLength: 120}} - type='text'/> + validateTimeOtherValue(value) { + return Validator.validate('time', value.other, [{type: 'required', data: true}, {type: 'numeric', data: true}]); + } - </div> - </ValidationForm> - ); + validateChoiceWithOther(value) { + let chosen = value.choice; + // if we have an empty multiple select we have a problem since it's required + if (value.choices) { + if (value.choices.length === 0) { + return Validator.validate('field', '', [{type: 'required', data: true}]); + } else { + // continuing validation with the first chosen value in case we have the 'Other' field + chosen = value.choices[0]; + } + } + if (chosen !== optionInputOther.OTHER) { + return Validator.validate('field', chosen, [{type: 'required', data: true}]); + } else { // when 'Other' was chosen, validate other value + return Validator.validate('field', value.other, [{type: 'required', data: true}]); + } } - submit() { - const {data: entitlementPool, previousData: previousEntitlementPool} = this.props; - this.props.onSubmit({entitlementPool, previousEntitlementPool}); + thresholdValueValidation(value, state) { + + let unit = state.data.thresholdUnits; + if (unit === thresholdUnitType.PERCENTAGE) { + return Validator.validate('thresholdValue', value, [ + {type: 'required', data: true}, + {type: 'numeric', data: true}, + {type: 'maximum', data: 100}, + {type: 'minimum', data: 0}]); + } else { + return Validator.validate('thresholdValue', value, [ + {type: 'numeric', data: true}, + {type: 'required', data: true}]); + } } + } export default EntitlementPoolsEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js index 4b21a2fea8..993ed48f2b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js @@ -1,27 +1,24 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; -import EntitlementPoolsListEditorView from './EntitlementPoolsListEditorView.jsx'; +import EntitlementPoolsListEditorView, {generateConfirmationMsg} from './EntitlementPoolsListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) => { let {entitlementPoolsList} = entitlementPool; @@ -39,13 +36,21 @@ const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onAddEntitlementPoolClick: () => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch), onEditEntitlementPoolClick: entitlementPool => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch, {entitlementPool}), - onDeleteEntitlementPool: entitlementPool => EntitlementPoolsActionHelper.openDeleteEntitlementPoolConfirm(dispatch, { - licenseModelId, - entitlementPool + onDeleteEntitlementPool: entitlementPool => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateConfirmationMsg(entitlementPool), + title: i18n('Warning'), + onConfirmed: ()=>EntitlementPoolsActionHelper.deleteEntitlementPool(dispatch, { + licenseModelId, + entitlementPoolId: entitlementPool.id, + version + }) + } }) }; }; 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 52df102503..07a6f21a1a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -6,10 +21,7 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import EntitlementPoolsEditor from './EntitlementPoolsEditor.js'; -import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; -import {optionsInputValues} from './EntitlementPoolsConstants'; -import EntitlementPoolsConfirmationModal from './EntitlementPoolsConfirmationModal.jsx'; - +import {extractUnits, extractValue} from './EntitlementPoolsConstants'; class EntitlementPoolsListEditorView extends React.Component { static propTypes = { @@ -33,35 +45,33 @@ class EntitlementPoolsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {licenseModelId, vendorName, 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 for {vendorName} License Model', {vendorName})} + title={i18n('Entitlement Pools', {vendorName})} plusButtonTitle={i18n('Add Entitlement Pool')} onAdd={onAddEntitlementPoolClick} filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} + onFilter={value => this.setState({localFilter: value})} isReadOnlyMode={isReadOnlyMode}> {this.filterList().map(entitlementPool => this.renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode))} </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='entitlement-pools-modal'> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal entitlement-pools-modal'> <Modal.Header> <Modal.Title>{`${isModalInEditMode ? i18n('Edit Entitlement Pool') : i18n('Create New Entitlement Pool')}`}</Modal.Title> </Modal.Header> <Modal.Body> { isDisplayModal && ( - <EntitlementPoolsEditor licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/> + <EntitlementPoolsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/> ) } </Modal.Body> </Modal> - - <EntitlementPoolsConfirmationModal licenseModelId={licenseModelId}/> </div> ); } @@ -92,14 +102,15 @@ class EntitlementPoolsListEditorView extends React.Component { className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode}> <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Name')}</div> - <div className='text name'>{name}</div> + <div ><div className='textEllipses text name'>{name}</div></div> </div> <div className='list-editor-item-view-field'> <div className='title'>{i18n('Entitlement')}</div> - <div className='entitlement-parameters'>{`${this.extractValue(aggregationFunction)} ${this.extractValue(entitlementMetric)} per ${this.extractValue(time)}`}</div> - <div className='entitlement-pools-count'>{`${thresholdValue ? thresholdValue : ''} ${this.extractUnits(thresholdUnits)}`}</div> + <div className='entitlement-parameters'>{`${extractValue(aggregationFunction)} ${extractValue(entitlementMetric)} per ${extractValue(time)}`}</div> + <div className='entitlement-pools-count'>{`${thresholdValue ? thresholdValue : ''} ${extractUnits(thresholdUnits)}`}</div> </div> <div className='list-editor-item-view-field'> @@ -115,18 +126,22 @@ class EntitlementPoolsListEditorView extends React.Component { ); } - - - extractUnits(units) { - if (units === undefined) {return '';} //TODO fix it later - return units === 'Absolute' ? '' : '%'; - } - - extractValue(item) { - if (item === undefined) {return '';} //TODO fix it later - - return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; - } } 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 subMsg = entitlementPoolToDelete + && entitlementPoolToDelete.referencingFeatureGroups + && entitlementPoolToDelete.referencingFeatureGroups.length > 0 ? + i18n('This entitlement pool is associated with one or more feature groups') : + ''; + return ( + <div> + <p>{msg}</p> + <p>{subMsg}</p> + </div> + ); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js index 63e351fce7..fefd823207 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './EntitlementPoolsConstants'; export default (state = [], action) => { switch (action.type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js index c2b269bcf9..c6249c98ca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js @@ -1,66 +1,78 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; import FeatureGroupEditorView from './FeatureGroupEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; -const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => { - +export const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => { + let {entitlementPoolsList = []} = entitlementPool; + let {licenseKeyGroupsList = []} = licenseKeyGroup; const {featureGroupEditor} = featureGroup; + let {data, selectedTab, genericFieldInfo, formReady} = featureGroupEditor; + const featureGroupId = data ? data.id : null; + const list = featureGroup.featureGroupsList; - let {data, selectedTab, selectedEntitlementPoolsButtonTab, selectedLicenseKeyGroupsButtonTab} = featureGroupEditor; + let previousData, FGNames = {}, isFormValid = true, invalidTabs = []; - let previousData; - const featureGroupId = data ? data.id : null; if (featureGroupId) { - previousData = featureGroup.featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId); + previousData = list.find(featureGroup => featureGroup.id === featureGroupId); + } + + for (let i = 0; i < list.length; i++) { + FGNames[list[i].name] = list[i].id; + } + + for (let field in genericFieldInfo) { + if (!genericFieldInfo[field].isValid) { + isFormValid = false; + let tabId = genericFieldInfo[field].tabId; + if (invalidTabs.indexOf(tabId) === -1) { + invalidTabs[invalidTabs.length] = genericFieldInfo[field].tabId; + } + } } - let {entitlementPoolsList = []} = entitlementPool; - let {licenseKeyGroupsList = []} = licenseKeyGroup; return { data, previousData, selectedTab, - selectedEntitlementPoolsButtonTab, - selectedLicenseKeyGroupsButtonTab, entitlementPoolsList, - licenseKeyGroupsList + licenseKeyGroupsList, + isFormValid, + formReady, + genericFieldInfo, + invalidTabs, + FGNames }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onTabSelect: tab => FeatureGroupsActionHelper.selectEntitlementPoolsEditorTab(dispatch, {tab}), - onEntitlementPoolsButtonTabSelect: buttonTab => FeatureGroupsActionHelper.selectFeatureGroupsEditorEntitlementPoolsButtonTab(dispatch, {buttonTab}), - onLicenseKeyGroupsButtonTabSelect: buttonTab => FeatureGroupsActionHelper.selectFeatureGroupsEditorLicenseKeyGroupsButtonTab(dispatch, {buttonTab}), - onDataChanged: deltaData => FeatureGroupsActionHelper.featureGroupsEditorDataChanged(dispatch, {deltaData}), onSubmit: (previousFeatureGroup, featureGroup) => { FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch); - FeatureGroupsActionHelper.saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}); - } + FeatureGroupsActionHelper.saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup, version}); + }, + onCancel: () => FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch), + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; export default connect(mapStateToProps, mapActionsToProps)(FeatureGroupEditorView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx index 6fecd16b71..5ae22cba95 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx @@ -1,27 +1,125 @@ +/*! + * 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 ValidationTabs from 'nfvo-components/input/validation/ValidationTabs.jsx'; -import ValidationTab from 'nfvo-components/input/validation/ValidationTab.jsx'; -import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; -import Button from 'react-bootstrap/lib/Button.js'; - -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import Tabs from 'nfvo-components/input/validation/Tabs.jsx'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; -import ListEditorViewItem from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.js'; -import {state as FeatureGroupStateConstants} from './FeatureGroupsConstants.js'; +import {state as FeatureGroupStateConstants, FG_EDITOR_FORM} from './FeatureGroupsConstants.js'; const FeatureGroupsPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, partNumber: React.PropTypes.string, - entitlementPoolsIds: React.PropTypes.array(React.PropTypes.string), - licenseKeyGroupsIds: React.PropTypes.array(React.PropTypes.string) + entitlementPoolsIds: React.PropTypes.arrayOf(React.PropTypes.string), + licenseKeyGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string) }); +const GeneralTab = ({data = {}, onDataChanged, genericFieldInfo, validateName}) => { + let {name, description, partNumber} = data; + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + groupClassName='field-section' + onChange={name => onDataChanged({name}, FG_EDITOR_FORM, {name: validateName})} + label={i18n('Name')} + data-test-id='create-fg-name' + value={name} + name='feature-group-name' + type='text' + isRequired={true} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} /> + <Input + groupClassName='field-section' + className='description-field' + onChange={description => onDataChanged({description}, FG_EDITOR_FORM)} + data-test-id='create-fg-description' + label={i18n('Description')} + value={description} + name='feature-group-description' + type='textarea' + isRequired={true} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} /> + <Input + groupClassName='field-section' + onChange={partNumber => onDataChanged({partNumber}, FG_EDITOR_FORM)} + label={i18n('Part Number')} + data-test-id='create-fg-part-number' + value={partNumber} + isRequired={true} + type='text' + isValid={genericFieldInfo.partNumber.isValid} + errorText={genericFieldInfo.partNumber.errorText} /> + </GridItem> + </GridSection> + ); +}; + +const EntitlementPoolsTab = ({entitlementPoolsList, data, onDataChanged, isReadOnlyMode}) => { + const dualBoxFilterTitle = { + left: i18n('Available Entitlement Pools'), + right: i18n('Selected Entitlement Pools') + }; + if (entitlementPoolsList.length > 0) { + return ( + <DualListboxView + isReadOnlyMode={isReadOnlyMode} + filterTitle={dualBoxFilterTitle} + selectedValuesList={data.entitlementPoolsIds} + availableList={entitlementPoolsList} + onChange={ selectedValuesList => onDataChanged( { entitlementPoolsIds: selectedValuesList }, FG_EDITOR_FORM )}/> + ); + } else { + return ( + <p>{i18n('There is no available entitlement pools')}</p> + ); + } +}; + +const LKGTab = ({licenseKeyGroupsList, data, onDataChanged, isReadOnlyMode}) => { + const dualBoxFilterTitle = { + left: i18n('Available License Key Groups'), + right: i18n('Selected License Key Groups') + }; + if (licenseKeyGroupsList.length > 0) { + return ( + <DualListboxView + isReadOnlyMode={isReadOnlyMode} + filterTitle={dualBoxFilterTitle} + selectedValuesList={data.licenseKeyGroupsIds} + availableList={licenseKeyGroupsList} + onChange={ selectedValuesList => onDataChanged( { licenseKeyGroupsIds: selectedValuesList }, FG_EDITOR_FORM )}/> + ); + } else { + return ( + <p>{i18n('There is no available licsense key groups')}</p> + ); + } +}; + class FeatureGroupEditorView extends React.Component { @@ -29,6 +127,7 @@ class FeatureGroupEditorView extends React.Component { data: FeatureGroupsPropType, previousData: FeatureGroupsPropType, isReadOnlyMode: React.PropTypes.bool, + FGNames: React.PropTypes.object, onSubmit: React.PropTypes.func, onCancel: React.PropTypes.func, @@ -36,11 +135,6 @@ class FeatureGroupEditorView extends React.Component { selectedTab: React.PropTypes.number, onTabSelect: React.PropTypes.func, - selectedEntitlementPoolsButtonTab: React.PropTypes.number, - selectedLicenseKeyGroupsButtonTab: React.PropTypes.number, - onEntitlementPoolsButtonTabSelect: React.PropTypes.func, - onLicenseKeyGroupsButtonTabSelect: React.PropTypes.func, - entitlementPoolsList: DualListboxView.propTypes.availableList, licenseKeyGroupsList: DualListboxView.propTypes.availableList }; @@ -49,8 +143,6 @@ class FeatureGroupEditorView extends React.Component { static defaultProps = { data: {}, selectedTab: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL, - selectedEntitlementPoolsButtonTab: FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS, - selectedLicenseKeyGroupsButtonTab: FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS }; state = { @@ -60,24 +152,39 @@ class FeatureGroupEditorView extends React.Component { render() { - let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + let {selectedTab, onTabSelect, isReadOnlyMode, invalidTabs, data, onDataChanged, genericFieldInfo, entitlementPoolsList, licenseKeyGroupsList} = this.props; return ( - <ValidationForm + <div> + { genericFieldInfo && <Form ref='validationForm' hasButtons={true} onSubmit={ () => this.submit() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(FG_EDITOR_FORM) } onReset={ () => this.props.onCancel() } labledButtons={true} isReadOnlyMode={isReadOnlyMode} name='feature-group-validation-form' className='feature-group-form'> - <ValidationTabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect}> - {this.renderGeneralTab()} - {this.renderEntitlementPoolsTab()} - {this.renderLicenseKeyGroupsTab()} - </ValidationTabs> - - </ValidationForm> + <Tabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect} invalidTabs={invalidTabs} id='vlmFGValTabs' > + <Tab eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL} title={i18n('General')} > + <GeneralTab data={data} onDataChanged={onDataChanged} genericFieldInfo={genericFieldInfo} validateName={(value)=> this.validateName(value)}/> + </Tab> + <Tab + eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS} + title={i18n('Entitlement Pools')} > + <EntitlementPoolsTab isReadOnlyMode={isReadOnlyMode} data={data} onDataChanged={onDataChanged} entitlementPoolsList={entitlementPoolsList} /> + </Tab> + <Tab + eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENSE_KEY_GROUPS} + title={i18n('License Key Groups')} > + <LKGTab isReadOnlyMode={isReadOnlyMode} data={data} onDataChanged={onDataChanged} licenseKeyGroupsList={licenseKeyGroupsList} /> + </Tab> + </Tabs> + + </Form> } + </div> ); } @@ -86,254 +193,14 @@ class FeatureGroupEditorView extends React.Component { this.props.onSubmit(previousFeatureGroup, featureGroup); } - renderGeneralTab() { - let {data = {}, onDataChanged} = this.props; - let {name, description, partNumber} = data; - return ( - <ValidationTab eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL} title={i18n('General')}> - <div> - <ValidationInput - groupClassName='field-section' - onChange={name => onDataChanged({name})} - ref='name' - label={i18n('Name')} - value={name} - name='feature-group-name' - validations={{maxLength: 120, required: true}} - type='text'/> - <ValidationInput - groupClassName='field-section' - className='description-field' - onChange={description => onDataChanged({description})} - ref='description' - label={i18n('Description')} - value={description} - name='feature-group-description' - validations={{maxLength: 1000, required: true}} - type='textarea'/> - <ValidationInput - groupClassName='field-section' - onChange={partNumber => onDataChanged({partNumber})} - label={i18n('Part Number')} - value={partNumber} - validations={{required: true}} - type='text'/> - </div> - </ValidationTab> - ); - } - - renderEntitlementPoolsTab() { - let {selectedEntitlementPoolsButtonTab, onEntitlementPoolsButtonTabSelect, entitlementPoolsList} = this.props; - if (entitlementPoolsList.length > 0) { - return ( - <ValidationTab - eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS} - title={i18n('Entitlement Pools')}> - <ButtonGroup> - { - this.renderButtonsTab( - FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS, - selectedEntitlementPoolsButtonTab, - i18n('Associated Entitlement Pools'), - onEntitlementPoolsButtonTabSelect - ) - } - { - this.renderButtonsTab( - FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.AVAILABLE_ENTITLEMENT_POOLS, - selectedEntitlementPoolsButtonTab, - i18n('Available Entitlement Pools'), - onEntitlementPoolsButtonTabSelect - ) - } - </ButtonGroup> - {this.renderEntitlementPoolsButtonTabContent(selectedEntitlementPoolsButtonTab)} - </ValidationTab> - ); - } else { - return ( - <ValidationTab - eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS} - title={i18n('Entitlement Pools')}> - <p>{i18n('There is no available entitlement pools.')}</p> - </ValidationTab> - ); - } - } - - renderLicenseKeyGroupsTab() { - let {selectedLicenseKeyGroupsButtonTab, onLicenseKeyGroupsButtonTabSelect, licenseKeyGroupsList} = this.props; - if (licenseKeyGroupsList.length > 0) { - return ( - <ValidationTab - eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS} - title={i18n('License Key Groups')}> - <ButtonGroup> - { - this.renderButtonsTab( - FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS, - selectedLicenseKeyGroupsButtonTab, - i18n('Associated License Key Groups'), - onLicenseKeyGroupsButtonTabSelect - ) - } - { - this.renderButtonsTab( - FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.AVAILABLE_LICENSE_KEY_GROUPS, - selectedLicenseKeyGroupsButtonTab, - i18n('Available License Key Groups'), - onLicenseKeyGroupsButtonTabSelect - ) - } - </ButtonGroup> - {this.renderLicenseKeyGroupsTabContent(selectedLicenseKeyGroupsButtonTab)} - </ValidationTab> - ); - } else { - return ( - <ValidationTab - eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS} - title={i18n('License Key Groups')}> - <p>{i18n('There is no available license key groups')}</p> - </ValidationTab>); - } - } - - renderButtonsTab(buttonTab, selectedButtonTab, title, onClick) { - const isSelected = buttonTab === selectedButtonTab; - return ( - <Button - className='button-tab' - active={isSelected} - onClick={() => !isSelected && onClick(buttonTab)}> - { title } - </Button> - ); - } - - renderEntitlementPoolsButtonTabContent(selectedFeatureGroupsButtonTab) { - const {entitlementPoolsList = [], data: {entitlementPoolsIds = []}} = this.props; - let dualBoxTitle = { - left: i18n('Available Entitlement Pools'), - right: i18n('Selected Entitlement Pools') - }; - - if (entitlementPoolsList.length) { - const {localEntitlementPoolsListFilter} = this.state; - let selectedEntitlementPools = entitlementPoolsIds.map(entitlementPoolId => entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId)); - - switch (selectedFeatureGroupsButtonTab) { - case FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS: - if (selectedEntitlementPools.length) { - return ( - <ListEditorView - className='thinner-list' - filterValue={localEntitlementPoolsListFilter} - onFilter={localEntitlementPoolsListFilter => this.setState({localEntitlementPoolsListFilter})}> - {this.filterAssociatedItems(selectedEntitlementPools, localEntitlementPoolsListFilter) - .map(entitlementPool => this.renderAssociatedListItem(entitlementPool - , FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS))} - </ListEditorView> - ); - } - else { - return ( - <div> - <br/>{i18n('There are currently no entitlement pools associated with this feature group. Click "Available Entitlement Pools" to associate.')} - </div> - ); - } - case FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.AVAILABLE_ENTITLEMENT_POOLS: - return ( - <DualListboxView - filterTitle={dualBoxTitle} - selectedValuesList={entitlementPoolsIds} - availableList={entitlementPoolsList} - onChange={ selectedValuesList => this.props.onDataChanged( { entitlementPoolsIds: selectedValuesList } )}/> - ); - } - } - } - - renderLicenseKeyGroupsTabContent(selectedFeatureGroupsButtonTab) { - const {licenseKeyGroupsList = [], data: {licenseKeyGroupsIds = []}} = this.props; - let dualBoxFilterTitle = { - left: i18n('Available License Key Groups'), - right: i18n('Selected License Key Groups') - }; - - if (licenseKeyGroupsList.length) { - const {localLicenseKeyGroupsListFilter} = this.state; - let selectedLicenseKeyGroups = licenseKeyGroupsIds.map(licenseKeyGroupId => licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId)); - - switch (selectedFeatureGroupsButtonTab) { - case FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS: - if (selectedLicenseKeyGroups.length) { - return ( - <ListEditorView - className='thinner-list' - filterValue={localLicenseKeyGroupsListFilter} - onFilter={localLicenseKeyGroupsListFilter => this.setState({localLicenseKeyGroupsListFilter})}> - {this.filterAssociatedItems(selectedLicenseKeyGroups, localLicenseKeyGroupsListFilter) - .map(licenseKeyGroup => this.renderAssociatedListItem(licenseKeyGroup - , FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS))} - </ListEditorView> - ); - } else { - return ( - <div className='no-items-msg'> - {i18n('There are currently no license key groups associated with this feature group. Click "Available License Key Groups" to associate.')} - </div> - ); - } - case FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.AVAILABLE_LICENSE_KEY_GROUPS: - return ( - <DualListboxView - filterTitle={dualBoxFilterTitle} - selectedValuesList={this.props.data.licenseKeyGroupsIds} - availableList={this.props.licenseKeyGroupsList} - onChange={ selectedValuesList => this.props.onDataChanged( { licenseKeyGroupsIds: selectedValuesList } )}/> - ); - } - } - } - - - renderAssociatedListItem(listItem, itemType) { - let {isReadOnlyMode} = this.props; - return ( - <ListEditorViewItem - key={listItem.id} - onDelete={() => this.deleteAssociatedItem(listItem.id, itemType)} - isReadOnlyMode={isReadOnlyMode}> - <div className='name'>{listItem.name}</div> - <div className='description'>{listItem.description}</div> - </ListEditorViewItem> - ); - } - - filterAssociatedItems(list, localList) { - if (localList) { - const filter = new RegExp(escape(localList), 'i'); - return list.filter(({name = '', description = ''}) => name.match(filter) || description.match(filter)); - } - else { - return list; - } - } - - deleteAssociatedItem(id, type) { - const {data: {licenseKeyGroupsIds = [], entitlementPoolsIds = []}} = this.props; - if (type === FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS) { - this.props.onDataChanged({licenseKeyGroupsIds: licenseKeyGroupsIds.filter(listId => listId !== id)}); - } else { - this.props.onDataChanged({entitlementPoolsIds: entitlementPoolsIds.filter(listId => listId !== id)}); - } + validateName(value) { + const {data: {id}, FGNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: FGNames}); + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Feature group by the name \'' + value + '\' already exists. Feature group name must be unique')}; } } export default FeatureGroupEditorView; - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js index 9ea5a31490..83473a30bb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js @@ -1,35 +1,33 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; + import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; -import FeatureGroupListEditorView from './FeatureGroupListEditorView.jsx'; +import FeatureGroupListEditorView, {generateConfirmationMsg} from './FeatureGroupListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; -const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => { +export const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => { const {featureGroupEditor: {data}, featureGroupsList} = featureGroup; - let {vendorName} = licenseModelEditor.data; + let {vendorName, version} = licenseModelEditor.data; let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); - return { vendorName, + version, featureGroupsModal: { show: Boolean(data), editMode: Boolean(data && data.id) @@ -42,13 +40,19 @@ const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => const mapActionsToProps = (dispatch, {licenseModelId}) => { return { - onDeleteFeatureGroupClick: (featureGroup) => FeatureGroupsActionHelper.openDeleteFeatureGroupConfirm(dispatch, {licenseModelId, featureGroup}), - onCancelFeatureGroupsEditor: () => FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch), - - onAddFeatureGroupClick: () => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId}), - onEditFeatureGroupClick: featureGroup => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, { + onDeleteFeatureGroupClick: (featureGroup, version) => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateConfirmationMsg(featureGroup), + title: i18n('Warning'), + onConfirmed: ()=>FeatureGroupsActionHelper.deleteFeatureGroup(dispatch, {featureGroupId: featureGroup.id, licenseModelId, version}) + } + }), + onAddFeatureGroupClick: (actualVersion) => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId, version: actualVersion}), + onEditFeatureGroupClick: (featureGroup, actualVersion) => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, { featureGroup, - licenseModelId + licenseModelId, + version: actualVersion }) }; }; 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 d998f9216f..bc0f5c71c0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -6,7 +21,6 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import FeatureGroupEditor from './FeatureGroupEditor.js'; -import FeatureGroupsConfirmationModal from './FeatureGroupsConfirmationModal.jsx'; class FeatureGroupListEditorView extends React.Component { static propTypes = { @@ -37,46 +51,45 @@ class FeatureGroupListEditorView extends React.Component { }; render() { - let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick} = this.props; + let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick, version} = this.props; const {localFilter} = this.state; - return ( <div className='feature-groups-list-editor'> <ListEditorView - title={i18n('Feature Groups for {vendorName} License Model', {vendorName})} + title={i18n('Feature Groups', {vendorName})} plusButtonTitle={i18n('Add Feature Group')} filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} - onAdd={() => onAddFeatureGroupClick()} + onFilter={value => this.setState({localFilter: value})} + onAdd={() => onAddFeatureGroupClick(version)} isReadOnlyMode={isReadOnlyMode}> - {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode))} + {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode, version))} </ListEditorView> - <Modal show={featureGroupsModal.show} bsSize='large' animation={true} className='feature-group-modal'> - <Modal.Header> - <Modal.Title>{`${featureGroupsModal.editMode ? i18n('Edit Feature Group') : i18n('Create New Feature Group')}`}</Modal.Title> - </Modal.Header> - <Modal.Body> - <FeatureGroupEditor - onCancel={() => this.closeFeatureGroupsEditor()} - licenseModelId={licenseModelId} - isReadOnlyMode={isReadOnlyMode}/> - </Modal.Body> - </Modal> - - <FeatureGroupsConfirmationModal licenseModelId={licenseModelId}/> + {featureGroupsModal.show && <Modal show={featureGroupsModal.show} bsSize='large' animation={true} + className='onborading-modal feature-group-modal'> + <Modal.Header> + <Modal.Title>{`${featureGroupsModal.editMode ? i18n('Edit Feature Group') : i18n('Create New Feature Group')}`}</Modal.Title> + </Modal.Header> + <Modal.Body> + <FeatureGroupEditor + version={version} + licenseModelId={licenseModelId} + isReadOnlyMode={isReadOnlyMode}/> + </Modal.Body> + </Modal> + } </div> ); } - renderFeatureGroupListItem(listItem, isReadOnlyMode) { + renderFeatureGroupListItem(listItem, isReadOnlyMode, version) { let {name, description, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem; return ( <ListEditorItemView key={listItem.id} - onDelete={() => this.deleteFeatureGroupItem(listItem)} - onSelect={() => this.editFeatureGroupItem(listItem)} + onDelete={() => this.deleteFeatureGroupItem(listItem, version)} + onSelect={() => this.editFeatureGroupItem(listItem, version)} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode}> <div className='list-editor-item-view-field'> @@ -120,17 +133,28 @@ class FeatureGroupListEditorView extends React.Component { } } - closeFeatureGroupsEditor() { - this.props.onCancelFeatureGroupsEditor(); - } - - editFeatureGroupItem(featureGroup) { - this.props.onEditFeatureGroupClick(featureGroup); + editFeatureGroupItem(featureGroup, version) { + this.props.onEditFeatureGroupClick(featureGroup, version); } - deleteFeatureGroupItem(featureGroup) { - this.props.onDeleteFeatureGroupClick(featureGroup); + deleteFeatureGroupItem(featureGroup, version) { + this.props.onDeleteFeatureGroupClick(featureGroup, version); } } 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 subMsg = featureGroupToDelete.referencingLicenseAgreements + && featureGroupToDelete.referencingLicenseAgreements.length > 0 ? + i18n('This feature group is associated with one ore more license agreements') : + ''; + return ( + <div> + <p>{msg}</p> + <p>{subMsg}</p> + </div> + ); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js index 3776c01263..a2015787a6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes as featureGroupsActionConstants} from './FeatureGroupsConstants.js'; @@ -25,22 +20,22 @@ import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseMod import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; -function baseUrl(licenseModelId) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/feature-groups`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/feature-groups`; } function fetchFeatureGroupsList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function deleteFeatureGroup(licenseModelId, featureGroupId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${featureGroupId}`); +function deleteFeatureGroup(licenseModelId, featureGroupId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${featureGroupId}`); } -function addFeatureGroup(licenseModelId, featureGroup) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function addFeatureGroup(licenseModelId, featureGroup, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: featureGroup.name, description: featureGroup.description, partNumber: featureGroup.partNumber, @@ -49,13 +44,13 @@ function addFeatureGroup(licenseModelId, featureGroup) { }); } -function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup) { +function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup, version) { const {licenseKeyGroupsIds = []} = featureGroup; const {licenseKeyGroupsIds: prevLicenseKeyGroupsIds = []} = previousFeatureGroup; const {entitlementPoolsIds = []} = featureGroup; const {entitlementPoolsIds: prevEntitlementPoolsIds = []} = previousFeatureGroup; - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${featureGroup.id}`, { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${featureGroup.id}`, { name: featureGroup.name, description: featureGroup.description, partNumber: featureGroup.partNumber, @@ -75,28 +70,37 @@ export default { })); }, - deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId}) { - return deleteFeatureGroup(licenseModelId, featureGroupId).then(() => dispatch({ + deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId, version}) { + return deleteFeatureGroup(licenseModelId, featureGroupId, version).then(() => dispatch({ type: featureGroupsActionConstants.DELETE_FEATURE_GROUPS, featureGroupId })); }, - saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}) { + saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup, version}) { if (previousFeatureGroup) { - return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup).then(() => dispatch({ - type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS, - featureGroup - })); + return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup, version).then(() =>{ + dispatch({ + type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS, + featureGroup + }); + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); + }); } else { - return addFeatureGroup(licenseModelId, featureGroup).then(response => dispatch({ - type: featureGroupsActionConstants.ADD_FEATURE_GROUPS, - featureGroup: { - ...featureGroup, - id: response.value - } - })); + return addFeatureGroup(licenseModelId, featureGroup, version).then(response => { + dispatch({ + type: featureGroupsActionConstants.ADD_FEATURE_GROUPS, + featureGroup: { + ...featureGroup, + id: response.value, + referencingLicenseAgreements: [] + } + }); + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); + }); } }, @@ -107,23 +111,9 @@ export default { }); }, - selectFeatureGroupsEditorEntitlementPoolsButtonTab(dispatch, {buttonTab}) { - dispatch({ - type: featureGroupsActionConstants.featureGroupsEditor.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB, - buttonTab - }); - }, - - selectFeatureGroupsEditorLicenseKeyGroupsButtonTab(dispatch, {buttonTab}) { - dispatch({ - type: featureGroupsActionConstants.featureGroupsEditor.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB, - buttonTab - }); - }, - - openFeatureGroupsEditor(dispatch, {featureGroup, licenseModelId}) { - EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId}); - LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId}); + openFeatureGroupsEditor(dispatch, {featureGroup, licenseModelId, version}) { + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); dispatch({ type: featureGroupsActionConstants.featureGroupsEditor.OPEN, featureGroup @@ -136,26 +126,6 @@ export default { }); }, - featureGroupsEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: featureGroupsActionConstants.featureGroupsEditor.DATA_CHANGED, - deltaData - }); - }, - - hideDeleteConfirm(dispatch) { - dispatch({ - type: featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM, - featureGroupToDelete: false - }); - }, - - openDeleteFeatureGroupConfirm(dispatch, {featureGroup}) { - dispatch({ - type: featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM, - featureGroupToDelete: featureGroup - }); - }, switchVersion(dispatch, {licenseModelId, version}) { LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx deleted file mode 100644 index 142ec3c4c8..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; - -function renderMsg(featureGroupToDelete) { - let name = featureGroupToDelete ? featureGroupToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); - let subMsg = featureGroupToDelete - && featureGroupToDelete.referencingLicenseAgreements - && featureGroupToDelete.referencingLicenseAgreements.length > 0 ? - i18n('This feature group is associated with one ore more license agreements') : - ''; - return ( - <div> - <p>{msg}</p> - <p>{subMsg}</p> - </div> - ); -}; - -const mapStateToProps = ({licenseModel: {featureGroup}}, {licenseModelId}) => { - let {featureGroupToDelete} = featureGroup; - const show = featureGroupToDelete !== false; - return { - show, - title: 'Warning!', - type: 'warning', - msg: renderMsg(featureGroupToDelete), - confirmationDetails: {featureGroupToDelete, licenseModelId} - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onConfirmed: ({featureGroupToDelete, licenseModelId}) => { - FeatureGroupsActionHelper.deleteFeatureGroup(dispatch, {featureGroupId: featureGroupToDelete.id, licenseModelId}); - FeatureGroupsActionHelper.hideDeleteConfirm(dispatch); - }, - onDeclined: () => { - FeatureGroupsActionHelper.hideDeleteConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js index e02c54595d..4c5a94f239 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -25,8 +20,6 @@ export const actionTypes = keyMirror({ ADD_FEATURE_GROUPS: null, EDIT_FEATURE_GROUPS: null, DELETE_FEATURE_GROUPS: null, - FEATURE_GROUPS_DELETE_CONFIRM: null, - ENTITLEMENT_POOLS_LIST_LOADED: null, @@ -34,27 +27,16 @@ export const actionTypes = keyMirror({ OPEN: null, CLOSE: null, DATA_CHANGED: null, - SELECT_TAB: null, - SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: null, - SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: null + SELECT_TAB: null } }); +export const FG_EDITOR_FORM = 'FG_EDITOR_FORM'; + export const state = keyMirror({ SELECTED_FEATURE_GROUP_TAB: { GENERAL: 1, ENTITLEMENT_POOLS: 2, - LICENCE_KEY_GROUPS: 3 - }, - SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: { - ASSOCIATED_ENTITLEMENT_POOLS: 1, - AVAILABLE_ENTITLEMENT_POOLS: 2 - }, - SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: { - ASSOCIATED_LICENSE_KEY_GROUPS: 1, - AVAILABLE_LICENSE_KEY_GROUPS: 2 - }, + LICENSE_KEY_GROUPS: 3 + } }); - - - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js index 576a5358e6..001bd20d44 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js @@ -1,24 +1,20 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ -import {actionTypes} from './FeatureGroupsConstants.js'; +import {actionTypes, FG_EDITOR_FORM, state as FeatureGroupStateConstants} from './FeatureGroupsConstants.js'; @@ -27,14 +23,28 @@ export default (state = {}, action) => { case actionTypes.featureGroupsEditor.OPEN: return { ...state, - data: action.featureGroup || {} - }; - case actionTypes.featureGroupsEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData + data: action.featureGroup || {}, + formReady: null, + formName: FG_EDITOR_FORM, + genericFieldInfo: { + 'description': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + }, + 'partNumber': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + }, + 'name': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + } } }; case actionTypes.featureGroupsEditor.CLOSE: diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js index 5cf3248919..3b5f1c55e4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './FeatureGroupsConstants.js'; export default (state = [], action) => { switch (action.type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js index 9616b60b76..efc4fb758f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js @@ -1,41 +1,36 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants.js'; import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; -function baseUrl(licenseModelId) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-agreements`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/license-agreements`; } function fetchLicenseAgreementList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function postLicenseAgreement(licenseModelId, licenseAgreement) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function postLicenseAgreement(licenseModelId, licenseAgreement, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: licenseAgreement.name, description: licenseAgreement.description, licenseTerm: licenseAgreement.licenseTerm, @@ -44,10 +39,10 @@ function postLicenseAgreement(licenseModelId, licenseAgreement) { }); } -function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement) { +function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement, version) { const {featureGroupsIds = []} = licenseAgreement; const {featureGroupsIds: prevFeatureGroupsIds = []} = previousLicenseAgreement; - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseAgreement.id}`, { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${licenseAgreement.id}`, { name: licenseAgreement.name, description: licenseAgreement.description, licenseTerm: licenseAgreement.licenseTerm, @@ -57,8 +52,8 @@ function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAg }); } -function deleteLicenseAgreement(licenseModelId, licenseAgreementId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseAgreementId}`); +function deleteLicenseAgreement(licenseModelId, licenseAgreementId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${licenseAgreementId}`); } export default { @@ -70,21 +65,14 @@ export default { })); }, - openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}) { - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}); + openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement, version}) { + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); dispatch({ type: licenseAgreementActionTypes.licenseAgreementEditor.OPEN, licenseAgreement }); }, - licenseAgreementEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: licenseAgreementActionTypes.licenseAgreementEditor.DATA_CHANGED, - deltaData - }); - }, - closeLicenseAgreementEditor(dispatch) { dispatch({ type: licenseAgreementActionTypes.licenseAgreementEditor.CLOSE @@ -92,9 +80,9 @@ export default { }, - saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement}) { + saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement, version}) { if (previousLicenseAgreement) { - return putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement).then(() => { + return putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement, version).then(() => { dispatch({ type: licenseAgreementActionTypes.EDIT_LICENSE_AGREEMENT, licenseAgreement @@ -102,7 +90,7 @@ export default { }); } else { - return postLicenseAgreement(licenseModelId, licenseAgreement).then(response => { + return postLicenseAgreement(licenseModelId, licenseAgreement, version).then(response => { dispatch({ type: licenseAgreementActionTypes.ADD_LICENSE_AGREEMENT, licenseAgreement: { @@ -114,8 +102,8 @@ export default { } }, - deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId}) { - return deleteLicenseAgreement(licenseModelId, licenseAgreementId).then(() => { + deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId, version}) { + return deleteLicenseAgreement(licenseModelId, licenseAgreementId, version).then(() => { dispatch({ type: licenseAgreementActionTypes.DELETE_LICENSE_AGREEMENT, licenseAgreementId @@ -130,31 +118,9 @@ export default { }); }, - selectLicenseAgreementEditorFeatureGroupsButtonTab(dispatch, {buttonTab}) { - dispatch({ - type: licenseAgreementActionTypes.licenseAgreementEditor.SELECT_FEATURE_GROUPS_BUTTONTAB, - buttonTab - }); - }, - - hideDeleteConfirm(dispatch) { - dispatch({ - type: licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM, - licenseAgreementToDelete: false - }); - }, - - openDeleteLicenseAgreementConfirm(dispatch, {licenseAgreement} ) { - dispatch({ - type: licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM, - licenseAgreementToDelete: licenseAgreement - }); - }, - switchVersion(dispatch, {licenseModelId, version}) { LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { this.fetchLicenseAgreementList(dispatch, {licenseModelId, version}); }); } }; - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx deleted file mode 100644 index 42f2407696..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; - -function renderMsg(licenseAgreementToDelete) { - let name = licenseAgreementToDelete ? licenseAgreementToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); - return( - <div> - <p>{msg}</p> - </div> - ); -}; - -const mapStateToProps = ({licenseModel: {licenseAgreement}}, {licenseModelId}) => { - let {licenseAgreementToDelete} = licenseAgreement; - const show = licenseAgreementToDelete !== false; - return { - show, - title: 'Warning!', - type: 'warning', - msg: renderMsg(licenseAgreementToDelete), - confirmationDetails: {licenseAgreementToDelete, licenseModelId} - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onConfirmed: ({licenseAgreementToDelete, licenseModelId}) => { - - LicenseAgreementActionHelper.deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId: licenseAgreementToDelete.id}); - LicenseAgreementActionHelper.hideDeleteConfirm(dispatch); - }, - onDeclined: () => { - LicenseAgreementActionHelper.hideDeleteConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js index af5c454e22..998d5f0e8d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js @@ -1,52 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; export const actionTypes = keyMirror({ LICENSE_AGREEMENT_LIST_LOADED: null, ADD_LICENSE_AGREEMENT: null, EDIT_LICENSE_AGREEMENT: null, DELETE_LICENSE_AGREEMENT: null, - LICENSE_AGREEMENT_DELETE_CONFIRM: null, licenseAgreementEditor: { OPEN: null, CLOSE: null, DATA_CHANGED: null, - SELECT_TAB: null, - SELECT_FEATURE_GROUPS_BUTTONTAB: null, + SELECT_TAB: null } }); +export const LA_EDITOR_FORM = 'LA_EDITOR_FORM'; + export const enums = keyMirror({ SELECTED_LICENSE_AGREEMENT_TAB: { GENERAL: 1, FEATURE_GROUPS: 2 - }, - - SELECTED_FEATURE_GROUPS_BUTTONTAB: { - ASSOCIATED_FEATURE_GROUPS: 1, - AVAILABLE_FEATURE_GROUPS: 2 } }); @@ -64,3 +55,11 @@ export const optionsInputValues = { {enum: 'Unlimited', title: 'Unlimited'} ] }; + +export const extractValue = (item) => { + if (item === undefined) { + return ''; + } //TODO fix it later + + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js index 6a3e4dbc73..aada8ddca1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js @@ -1,31 +1,29 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; import LicenseAgreementEditorView from './LicenseAgreementEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}}) => { - let {data, selectedTab, selectedFeatureGroupsButtonTab} = licenseAgreement.licenseAgreementEditor; + let {data, selectedTab, genericFieldInfo, formReady} = licenseAgreement.licenseAgreementEditor; + const list = licenseAgreement.licenseAgreementList; + const LANames = {}; let previousData; const licenseAgreementId = data ? data.id : null; @@ -33,27 +31,47 @@ export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}} previousData = licenseAgreement.licenseAgreementList.find(licenseAgreement => licenseAgreement.id === licenseAgreementId); } + for (let i = 0; i < list.length; i++) { + LANames[list[i].name] = list[i].id; + } + const {featureGroupsList = []} = featureGroup; + let isFormValid = true; + let invalidTabs = []; + for (let field in genericFieldInfo) { + if (!genericFieldInfo[field].isValid) { + isFormValid = false; + let tabId = genericFieldInfo[field].tabId; + if (invalidTabs.indexOf(tabId) === -1) { + invalidTabs[invalidTabs.length] = genericFieldInfo[field].tabId; + } + } + } + return { data, previousData, selectedTab, - selectedFeatureGroupsButtonTab, - featureGroupsList + featureGroupsList, + LANames, + genericFieldInfo, + isFormValid, + formReady, + invalidTabs }; }; -export const mapActionsToProps = (dispatch, {licenseModelId}) => { +export const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { - onDataChanged: deltaData => LicenseAgreementActionHelper.licenseAgreementEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onTabSelect: tab => LicenseAgreementActionHelper.selectLicenseAgreementEditorTab(dispatch, {tab}), - onFeatureGroupsButtonTabSelect: buttonTab => LicenseAgreementActionHelper.selectLicenseAgreementEditorFeatureGroupsButtonTab(dispatch, {buttonTab}), onCancel: () => LicenseAgreementActionHelper.closeLicenseAgreementEditor(dispatch), onSubmit: ({previousLicenseAgreement, licenseAgreement}) => { LicenseAgreementActionHelper.closeLicenseAgreementEditor(dispatch); - LicenseAgreementActionHelper.saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement}); - } + LicenseAgreementActionHelper.saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement, version}); + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js index 74e2f6e8c1..e02935c579 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js @@ -1,40 +1,55 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes, defaultState} from './LicenseAgreementConstants.js'; +import {actionTypes, defaultState, LA_EDITOR_FORM, enums as LicenseAgreementEnums} from './LicenseAgreementConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.licenseAgreementEditor.OPEN: return { ...state, + formReady: null, + formName: LA_EDITOR_FORM, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + }, + 'requirementsAndConstrains' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + }, + 'licenseTerm' : { + isValid: true, + errorText: '', + validations: [], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}], + tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL + } + }, data: action.licenseAgreement ? { ...action.licenseAgreement } : defaultState.LICENSE_AGREEMENT_EDITOR_DATA }; - case actionTypes.licenseAgreementEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; case actionTypes.licenseAgreementEditor.CLOSE: return {}; case actionTypes.licenseAgreementEditor.SELECT_TAB: @@ -42,11 +57,6 @@ export default (state = {}, action) => { ...state, selectedTab: action.tab }; - case actionTypes.licenseAgreementEditor.SELECT_FEATURE_GROUPS_BUTTONTAB: - return { - ...state, - selectedFeatureGroupsButtonTab: action.buttonTab - }; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx index b21f943fed..67a3333a3a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx @@ -1,17 +1,36 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; -import Button from 'react-bootstrap/lib/Button.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationTabs from 'nfvo-components/input/validation/ValidationTabs.jsx'; -import ValidationTab from 'nfvo-components/input/validation/ValidationTab.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Tabs from 'nfvo-components/input/validation/Tabs.jsx'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx'; -import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; -import ListEditorViewItem from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.js'; -import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues} from './LicenseAgreementConstants.js'; +import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues, LA_EDITOR_FORM} from './LicenseAgreementConstants.js'; +const dualBoxFilterTitle = { + left: i18n('Available Feature Groups'), + right: i18n('Selected Feature Groups') +}; const LicenseAgreementPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -19,14 +38,77 @@ const LicenseAgreementPropType = React.PropTypes.shape({ description: React.PropTypes.string, requirementsAndConstrains: React.PropTypes.string, licenseTerm: React.PropTypes.object, - featureGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string) + featureGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string), + version: React.PropTypes.object }); + +const GeneralTabContent = ({data, genericFieldInfo, onDataChanged, validateName, validateLTChoice}) => { + let {name, description, requirementsAndConstrains, licenseTerm} = data; + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} + onChange={name => onDataChanged({name}, LA_EDITOR_FORM, { name: validateName })} + label={i18n('Name')} + value={name} + data-test-id='create-la-name' + name='license-agreement-name' + isRequired={true} + type='text'/> + <Input + isValid={genericFieldInfo.requirementsAndConstrains.isValid} + errorText={genericFieldInfo.requirementsAndConstrains.errorText} + onChange={requirementsAndConstrains => onDataChanged({requirementsAndConstrains}, LA_EDITOR_FORM)} + label={i18n('Requirements and Constraints')} + value={requirementsAndConstrains} + data-test-id='create-la-requirements-constants' + name='license-agreement-requirements-and-constraints' + type='textarea'/> + <Input + label={i18n('License Term')} + type='select' + value={licenseTerm && licenseTerm.choice} + isRequired={true} + onChange={e => { + const selectedIndex = e.target.selectedIndex; + const licenseTerm = e.target.options[selectedIndex].value; + onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}}, LA_EDITOR_FORM, { licenseTerm: validateLTChoice }); + }} + isValid={genericFieldInfo.licenseTerm.isValid} + errorText={genericFieldInfo.licenseTerm.errorText} + className='input-options-select' + groupClassName='bootstrap-input-options' + data-test-id='create-la-license-term' > + {LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + <GridItem colSpan={2} stretch> + <Input + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + onChange={description => onDataChanged({description}, LA_EDITOR_FORM)} + label={i18n('Description')} + value={description} + overlayPos='bottom' + data-test-id='create-la-description' + name='license-agreement-description' + isRequired={true} + type='textarea'/> + </GridItem> + </GridSection> + ); +}; + class LicenseAgreementEditorView extends React.Component { static propTypes = { data: LicenseAgreementPropType, previousData: LicenseAgreementPropType, + LANames: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, @@ -42,7 +124,6 @@ class LicenseAgreementEditorView extends React.Component { static defaultProps = { selectedTab: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL, - selectedFeatureGroupsButtonTab: LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS, data: {} }; @@ -51,21 +132,44 @@ class LicenseAgreementEditorView extends React.Component { }; render() { - let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + let {selectedTab, onTabSelect, isReadOnlyMode, featureGroupsList, data, onDataChanged, genericFieldInfo} = this.props; return ( - <ValidationForm - ref='validationForm' - hasButtons={true} - onSubmit={ () => this.submit() } - onReset={ () => this.props.onCancel() } - labledButtons={true} - isReadOnlyMode={isReadOnlyMode} - className='license-agreement-form'> - <ValidationTabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect}> - {this.renderGeneralTab()} - {this.renderFeatureGroupsTab()} - </ValidationTabs> - </ValidationForm> + <div> + {genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(LA_EDITOR_FORM) } + className='license-agreement-form'> + <Tabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect} invalidTabs={this.props.invalidTabs} > + <Tab + eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL} + data-test-id='general-tab' + title={i18n('General')}> + <GeneralTabContent data={data} genericFieldInfo={genericFieldInfo} onDataChanged={onDataChanged} validateLTChoice={(value)=>this.validateLTChoice(value)} + validateName={(value)=>this.validateName(value)}/> + </Tab> + <Tab + eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS} + data-test-id='feature-group-tab' + title={i18n('Feature Groups')}> + {featureGroupsList.length > 0 ? + <DualListboxView + isReadOnlyMode={isReadOnlyMode} + filterTitle={dualBoxFilterTitle} + selectedValuesList={data.featureGroupsIds} + availableList={featureGroupsList} + onChange={ selectedValuesList => onDataChanged( { featureGroupsIds: selectedValuesList }, LA_EDITOR_FORM )}/> : + <p>{i18n('There is no available feature groups')}</p>} + </Tab> + </Tabs> + </Form>} + </div> ); } @@ -74,173 +178,19 @@ class LicenseAgreementEditorView extends React.Component { this.props.onSubmit({licenseAgreement, previousLicenseAgreement}); } - renderGeneralTab() { - let {data = {}, onDataChanged} = this.props; - let {name, description, requirementsAndConstrains, licenseTerm} = data; - return ( - <ValidationTab - eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL} - title={i18n('General')}> - <div className='license-agreement-form-row'> - <div className='license-agreement-form-col'> - <ValidationInput - onChange={name => onDataChanged({name})} - label={i18n('Name')} - value={name} - name='license-agreement-name' - validations={{maxLength: 25, required: true}} - type='text'/> - <ValidationInput - onChange={requirementsAndConstrains => onDataChanged({requirementsAndConstrains})} - label={i18n('Requirements and Constraints')} - value={requirementsAndConstrains} - name='license-agreement-requirements-and-constraints' - validations={{maxLength: 1000}} - type='textarea'/> - </div> - <ValidationInput - onChange={description => onDataChanged({description})} - label={i18n('Description')} - value={description} - name='license-agreement-description' - validations={{maxLength: 1000, required: true}} - type='textarea'/> - </div> - <div className='license-agreement-form-row'> - <ValidationInput - onEnumChange={licenseTerm => onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}})} - selectedEnum={licenseTerm && licenseTerm.choice} - validations={{required: true}} - type='select' - label={i18n('License Term')} - values={LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE}/> - </div> - </ValidationTab> - ); - } - - renderFeatureGroupsTab() { - let {onFeatureGroupsButtonTabSelect, selectedFeatureGroupsButtonTab, featureGroupsList} = this.props; - if (featureGroupsList.length > 0) { - return ( - <ValidationTab - eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS} - title={i18n('Feature Groups')}> - <ButtonGroup> - { - this.renderFeatureGroupsButtonTab( - LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.ASSOCIATED_FEATURE_GROUPS, - selectedFeatureGroupsButtonTab, - i18n('Associated Feature Groups'), - onFeatureGroupsButtonTabSelect - ) - } - { - this.renderFeatureGroupsButtonTab( - LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS, - selectedFeatureGroupsButtonTab, - i18n('Available Feature Groups'), - onFeatureGroupsButtonTabSelect - ) - } - </ButtonGroup> - {this.renderFeatureGroupsButtonTabContent(selectedFeatureGroupsButtonTab)} - </ValidationTab> - ); - } else { - return ( - <ValidationTab - eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS} - title={i18n('Feature Groups')}> - <p>{i18n('There is no available feature groups')}</p> - </ValidationTab> - ); + validateLTChoice(value) { + if (!value.choice) { + return {isValid: false, errorText: i18n('Field is required')}; } + return {isValid: true, errorText: ''}; } - renderFeatureGroupsButtonTabContent(selectedFeatureGroupsButtonTab) { - const {featureGroupsList = [], data: {featureGroupsIds = []}} = this.props; - const {localFeatureGroupsListFilter} = this.state; - let selectedFeatureGroups = featureGroupsIds.map(featureGroupId => featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId)); - - const dualBoxFilterTitle = { - left: i18n('Available Feature Groups'), - right: i18n('Selected Feature Groups') - }; - - switch (selectedFeatureGroupsButtonTab) { - case LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.ASSOCIATED_FEATURE_GROUPS: - if (!selectedFeatureGroups.length) { - return ( - <div className='no-items-msg'> - {i18n('There are currently no feature groups associated with this license agreement. Click "Available Feature Groups" to associate.')} - </div> - ); - } - if (featureGroupsList.length) { - return ( - <ListEditorView - className='thinner-list' - filterValue={localFeatureGroupsListFilter} - onFilter={localFeatureGroupsListFilter => this.setState({localFeatureGroupsListFilter})}> - {this.filterAssociatedFeatureGroupsList(selectedFeatureGroups).map(featureGroup => this.renderAssociatedFeatureGroupListItem(featureGroup))} - </ListEditorView> - ); - } - return; - case LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS: - return ( - <DualListboxView - filterTitle={dualBoxFilterTitle} - selectedValuesList={this.props.data.featureGroupsIds} - availableList={this.props.featureGroupsList} - onChange={ selectedValuesList => this.props.onDataChanged( { featureGroupsIds: selectedValuesList } )}/> - ); - } - } + validateName(value) { + const {data: {id}, LANames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: LANames}); - renderFeatureGroupsButtonTab(buttonTab, selectedButtonTab, title, onClick) { - const isSelected = buttonTab === selectedButtonTab; - return ( - <Button - className='button-tab' - active={isSelected} - onClick={() => !isSelected && onClick(buttonTab)}> - { title } - </Button> - ); - } - - renderAssociatedFeatureGroupListItem({id, name, entitlementPoolsIds = [], licenseKeyGroupsIds = []}) { - const {onDataChanged, data: {featureGroupsIds}, isReadOnlyMode} = this.props; - return ( - <ListEditorViewItem - key={id} - onDelete={() => onDataChanged({featureGroupsIds: featureGroupsIds.filter(featureGroupId => featureGroupId !== id)})} - isReadOnlyMode={isReadOnlyMode}> - <div className='name'>{name}</div> - <div className='inner-objects-count'>{ - i18n( - 'Entitlement Pools({entitlementPoolsCounter}), License Key Groups({licenseKeyGroupsCount})', - { - entitlementPoolsCounter: entitlementPoolsIds.length, - licenseKeyGroupsCount: licenseKeyGroupsIds.length - } - ) - }</div> - </ListEditorViewItem> - ); - } - - filterAssociatedFeatureGroupsList(featureGroupsList) { - let {localFeatureGroupsListFilter} = this.state; - if (localFeatureGroupsListFilter) { - const filter = new RegExp(escape(localFeatureGroupsListFilter), 'i'); - return featureGroupsList.filter(({name}) => name.match(filter)); - } - else { - return featureGroupsList; - } + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('License Agreement by the name \'' + value + '\' already exists. License agreement name must be unique')}; } } 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 ca18bfab79..373694f2bf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js @@ -1,39 +1,35 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; -import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; import LicenseAgreementListEditorView from './LicenseAgreementListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; -import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; const mapStateToProps = ({licenseModel: {licenseAgreement, licenseModelEditor}}) => { let {licenseAgreementList} = licenseAgreement; let {data} = licenseAgreement.licenseAgreementEditor; - let {vendorName} = licenseModelEditor.data; + let {vendorName, version} = licenseModelEditor.data; let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); return { vendorName, + version, licenseAgreementList, isReadOnlyMode, isDisplayModal: Boolean(data), @@ -43,16 +39,16 @@ const mapStateToProps = ({licenseModel: {licenseAgreement, licenseModelEditor}}) const mapActionsToProps = (dispatch, {licenseModelId}) => { return { - onAddLicenseAgreementClick: () => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId}), - onEditLicenseAgreementClick: licenseAgreement => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}), - onDeleteLicenseAgreement: licenseAgreement => LicenseAgreementActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseAgreement}), - onCallVCAction: action => { - LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => { - LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId}); - }); - }, - switchLicenseModelVersion: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}), - onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch) + onAddLicenseAgreementClick: (version) => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, version}), + onEditLicenseAgreementClick: (licenseAgreement, version) => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement, version}), + onDeleteLicenseAgreement: (licenseAgreement, version) => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Are you sure you want to delete "{name}"?', {name: 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 4d7e704ba3..776b04b8eb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx @@ -1,15 +1,26 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; - import i18n from 'nfvo-utils/i18n/i18n.js'; import Modal from 'nfvo-components/modal/Modal.jsx'; import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import LicenseAgreementEditor from './LicenseAgreementEditor.js'; -import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; -import {optionsInputValues} from './LicenseAgreementConstants'; -import LicenseAgreementConfirmationModal from './LicenseAgreementConfirmationModal.jsx'; - +import {extractValue} from './LicenseAgreementConstants'; class LicenseAgreementListEditorView extends React.Component { static propTypes = { @@ -22,7 +33,6 @@ class LicenseAgreementListEditorView extends React.Component { onAddLicenseAgreementClick: React.PropTypes.func, onEditLicenseAgreementClick: React.PropTypes.func, onDeleteLicenseAgreement: React.PropTypes.func, - onCallVCAction: React.PropTypes.func }; static defaultProps = { @@ -34,35 +44,33 @@ class LicenseAgreementListEditorView extends React.Component { }; render() { - const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + const {licenseModelId, vendorName, 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 for {vendorName} License Model', {vendorName})} - plusButtonTitle={i18n('Add License Agreement')} - onAdd={onAddLicenseAgreementClick} - filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} - isReadOnlyMode={isReadOnlyMode}> - {this.filterList().map(licenseAgreement => this.renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode))} - </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='license-agreement-modal'> + <ListEditorView + title={i18n('License Agreements', {vendorName})} + plusButtonTitle={i18n('Add License Agreement')} + onAdd={() => onAddLicenseAgreementClick(version)} + filterValue={localFilter} + onFilter={value => this.setState({localFilter: value})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(licenseAgreement => this.renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode, version))} + </ListEditorView> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal license-agreement-modal'> <Modal.Header> <Modal.Title>{`${isModalInEditMode ? i18n('Edit License Agreement') : i18n('Create New License Agreement')}`}</Modal.Title> </Modal.Header> <Modal.Body> { isDisplayModal && ( - <LicenseAgreementEditor licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode} /> + <LicenseAgreementEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode} /> ) } </Modal.Body> </Modal> - <LicenseAgreementConfirmationModal licenseModelId={licenseModelId}/> - </div> ); } @@ -73,7 +81,7 @@ class LicenseAgreementListEditorView extends React.Component { if (localFilter.trim()) { const filter = new RegExp(escape(localFilter), 'i'); return licenseAgreementList.filter(({name = '', description = '', licenseTerm = ''}) => { - return escape(name).match(filter) || escape(description).match(filter) || escape(this.extractValue(licenseTerm)).match(filter); + return escape(name).match(filter) || escape(description).match(filter) || escape(extractValue(licenseTerm)).match(filter); }); } else { @@ -81,14 +89,14 @@ class LicenseAgreementListEditorView extends React.Component { } } - renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode) { + renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode, version) { let {id, name, description, licenseTerm, featureGroupsIds = []} = licenseAgreement; let {onEditLicenseAgreementClick, onDeleteLicenseAgreement} = this.props; return ( <ListEditorItemView key={id} - onSelect={() => onEditLicenseAgreementClick(licenseAgreement)} - onDelete={() => onDeleteLicenseAgreement(licenseAgreement)} + onSelect={() => onEditLicenseAgreementClick(licenseAgreement, version)} + onDelete={() => onDeleteLicenseAgreement(licenseAgreement, version)} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode}> <div className='list-editor-item-view-field'> @@ -98,7 +106,7 @@ class LicenseAgreementListEditorView extends React.Component { <div className='list-editor-item-view-field'> <div className='list-editor-item-view-field-tight'> <div className='title'>{i18n('Type')}</div> - <div className='text type'>{this.extractValue(licenseTerm)}</div> + <div className='text type'>{extractValue(licenseTerm)}</div> </div> <div className='list-editor-item-view-field-tight'> <div className='title'>{i18n('Feature')}</div> @@ -113,14 +121,6 @@ class LicenseAgreementListEditorView extends React.Component { </ListEditorItemView> ); } - - extractValue(item) { - if (item === undefined) { - return ''; - } //TODO fix it later - - return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; - } } export default LicenseAgreementListEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js index 5b5fa00df1..e6a8f34b58 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js index 50ac2c85a3..dd2a5c6003 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js @@ -1,44 +1,39 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes as licenseKeyGroupsConstants} from './LicenseKeyGroupsConstants.js'; import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; -function baseUrl(licenseModelId) { +function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-key-groups`; + const {id: versionId} = version; + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/license-key-groups`; } function fetchLicenseKeyGroupsList(licenseModelId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId) { - return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseKeyGroupId}`); +function deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId, version) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${licenseKeyGroupId}`); } -function postLicenseKeyGroup(licenseModelId, licenseKeyGroup) { - return RestAPIUtil.create(baseUrl(licenseModelId), { +function postLicenseKeyGroup(licenseModelId, licenseKeyGroup, version) { + return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: licenseKeyGroup.name, description: licenseKeyGroup.description, operationalScope: licenseKeyGroup.operationalScope, @@ -46,8 +41,8 @@ function postLicenseKeyGroup(licenseModelId, licenseKeyGroup) { }); } -function putLicenseKeyGroup(licenseModelId, licenseKeyGroup) { - return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseKeyGroup.id}`, { +function putLicenseKeyGroup(licenseModelId, licenseKeyGroup, version) { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${licenseKeyGroup.id}`, { name: licenseKeyGroup.name, description: licenseKeyGroup.description, operationalScope: licenseKeyGroup.operationalScope, @@ -77,9 +72,9 @@ export default { }); }, - saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}) { + saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup, version}) { if (previousLicenseKeyGroup) { - return putLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(() => { + return putLicenseKeyGroup(licenseModelId, licenseKeyGroup, version).then(() => { dispatch({ type: licenseKeyGroupsConstants.EDIT_LICENSE_KEY_GROUP, licenseKeyGroup @@ -87,11 +82,12 @@ export default { }); } else { - return postLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(response => { + return postLicenseKeyGroup(licenseModelId, licenseKeyGroup, version).then(response => { dispatch({ type: licenseKeyGroupsConstants.ADD_LICENSE_KEY_GROUP, licenseKeyGroup: { ...licenseKeyGroup, + referencingFeatureGroups: [], id: response.value } }); @@ -101,8 +97,8 @@ export default { }, - deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId}){ - return deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId).then(()=> { + deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId, version}){ + return deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId, version).then(()=> { dispatch({ type: licenseKeyGroupsConstants.DELETE_LICENSE_KEY_GROUP, licenseKeyGroupId @@ -110,13 +106,6 @@ export default { }); }, - licenseKeyGroupEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.DATA_CHANGED, - deltaData - }); - }, - hideDeleteConfirm(dispatch) { dispatch({ type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM, diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx deleted file mode 100644 index 2413db51d0..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; - -function renderMsg(licenseKeyGroupToDelete) { - let name = licenseKeyGroupToDelete ? licenseKeyGroupToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); - let subMsg = licenseKeyGroupToDelete - && licenseKeyGroupToDelete.referencingFeatureGroups - && licenseKeyGroupToDelete.referencingFeatureGroups.length > 0 ? - i18n('This license key group is associated with one or more feature groups') : - ''; - return( - <div> - <p>{msg}</p> - <p>{subMsg}</p> - </div> - ); -}; - -const mapStateToProps = ({licenseModel: {licenseKeyGroup}}, {licenseModelId}) => { - let {licenseKeyGroupToDelete} = licenseKeyGroup; - const show = licenseKeyGroupToDelete !== false; - return { - show, - title: 'Warning!', - type: 'warning', - msg: renderMsg(licenseKeyGroupToDelete), - confirmationDetails: {licenseKeyGroupToDelete, licenseModelId} - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onConfirmed: ({licenseKeyGroupToDelete, licenseModelId}) => { - - LicenseKeyGroupsActionHelper.deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId:licenseKeyGroupToDelete.id}); - LicenseKeyGroupsActionHelper.hideDeleteConfirm(dispatch); - }, - onDeclined: () => { - LicenseKeyGroupsActionHelper.hideDeleteConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js index d32bc52744..50d1fe8625 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js @@ -1,25 +1,21 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; export const actionTypes = keyMirror({ @@ -42,6 +38,8 @@ export const defaultState = { } }; +export const LKG_FORM_NAME = 'LKGFORM'; + export const optionsInputValues = { OPERATIONAL_SCOPE: [ {enum: '', title: i18n('please select…')}, @@ -61,4 +59,21 @@ export const optionsInputValues = { ] }; +export const extractValue = (item) => { + if (item === undefined) {return '';} //TODO fix it later + + return item ? item === optionInputOther.OTHER ? item : InputOptions.getTitleByName(optionsInputValues, item) : ''; +}; +export const getOperationalScopes = (operationalScope) => { + if(operationalScope.choices.toString() === i18n(optionInputOther.OTHER) && operationalScope.other !== '') { + return operationalScope.other; + } + else { + let allOpScopes = ''; + for (let opScope of operationalScope.choices) { + allOpScopes += allOpScopes === '' ? InputOptions.getTitleByName(optionsInputValues, opScope) : `, ${InputOptions.getTitleByName(optionsInputValues, opScope)}`; + } + return allOpScopes; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js index 3940ec594a..aef1532dc1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js @@ -1,52 +1,60 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; import LicenseKeyGroupsEditorView from './LicenseKeyGroupsEditorView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => { - let {data} = licenseKeyGroup.licenseKeyGroupsEditor; + let {data, genericFieldInfo, formReady} = licenseKeyGroup.licenseKeyGroupsEditor; - let previousData; + let previousData, LKGNames = {}; const licenseKeyGroupId = data ? data.id : null; if(licenseKeyGroupId) { previousData = licenseKeyGroup.licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId); } + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + const list = licenseKeyGroup.licenseKeyGroupsList; + for (let i = 0; i < list.length; i++) { + LKGNames[list[i].name] = list[i].id; + } + return { data, - previousData + previousData, + genericFieldInfo, + isFormValid, + formReady, + LKGNames }; }; -const mapActionsToProps = (dispatch, {licenseModelId}) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { - onDataChanged: deltaData => LicenseKeyGroupsActionHelper.licenseKeyGroupEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch), onSubmit: ({previousLicenseKeyGroup, licenseKeyGroup}) => { LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch); - LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}); - } + LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup, version}); + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js index a74498269a..090c971c65 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js @@ -1,42 +1,53 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes, defaultState} from './LicenseKeyGroupsConstants.js'; +import {actionTypes, defaultState, LKG_FORM_NAME} from './LicenseKeyGroupsConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.licenseKeyGroupsEditor.OPEN: return { ...state, - data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor + data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor, + formReady: null, + formName: LKG_FORM_NAME, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'type' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'operationalScope' : { + isValid: true, + errorText: '', + validations: [] + } + } }; case actionTypes.licenseKeyGroupsEditor.CLOSE: return {}; - case actionTypes.licenseKeyGroupsEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx index 102e713060..b95efd0f9c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx @@ -1,10 +1,29 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import {optionsInputValues as licenseKeyGroupOptionsInputValues} from './LicenseKeyGroupsConstants.js'; -import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +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 {optionsInputValues as licenseKeyGroupOptionsInputValues, LKG_FORM_NAME} from './LicenseKeyGroupsConstants.js'; +import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; const LicenseKeyGroupPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -17,10 +36,80 @@ const LicenseKeyGroupPropType = React.PropTypes.shape({ type: React.PropTypes.string }); +const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, validateName, validateOperationalScope}) => { + let {name, description, operationalScope, type} = data; + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name}, LKG_FORM_NAME, {name: validateName})} + label={i18n('Name')} + data-test-id='create-lkg-name' + value={name} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} + isRequired={true} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={true} + isRequired={true} + onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}}, + LKG_FORM_NAME, {operationalScope: validateOperationalScope})} + onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], + other: operationalScope}}, LKG_FORM_NAME, {operationalScope: validateOperationalScope})} + label={i18n('Operational Scope')} + data-test-id='create-lkg-operational-scope' + type='select' + multiSelectedEnum={operationalScope && operationalScope.choices} + otherValue={operationalScope && operationalScope.other} + values={licenseKeyGroupOptionsInputValues.OPERATIONAL_SCOPE} + isValid={genericFieldInfo.operationalScope.isValid} + errorText={genericFieldInfo.operationalScope.errorText} /> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={description => onDataChanged({description}, LKG_FORM_NAME)} + label={i18n('Description')} + data-test-id='create-lkg-description' + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + isRequired={true} + type='textarea' + overlayPos='bottom' /> + </GridItem> + <GridItem colSpan={2}> + <Input + isRequired={true} + onChange={e => { const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({type: val}, LKG_FORM_NAME);}} + value={type} + label={i18n('Type')} + data-test-id='create-lkg-type' + isValid={genericFieldInfo.type.isValid} + errorText={genericFieldInfo.type.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + { + licenseKeyGroupOptionsInputValues.TYPE.map(type => + (<option key={type.enum} value={type.enum}>{type.title}</option>)) + } + </Input> + </GridItem> + </GridSection> + ); +}; + class LicenseKeyGroupsEditorView extends React.Component { static propTypes = { data: LicenseKeyGroupPropType, previousData: LicenseKeyGroupPropType, + LKGNames: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, @@ -32,54 +121,29 @@ class LicenseKeyGroupsEditorView extends React.Component { }; render() { - let {data = {}, onDataChanged, isReadOnlyMode} = this.props; - let {name, description, operationalScope, type} = data; + let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo} = this.props; return ( - <ValidationForm + <div> + { genericFieldInfo && + <Form ref='validationForm' hasButtons={true} onSubmit={ () => this.submit() } onReset={ () => this.props.onCancel() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(LKG_FORM_NAME) } labledButtons={true} isReadOnlyMode={isReadOnlyMode} className='license-key-groups-form'> - <div className='license-key-groups-form-row'> - <ValidationInput - onChange={name => onDataChanged({name})} - ref='name' - label={i18n('Name')} - value={name} - validations={{maxLength: 120, required: true}} - type='text'/> - <ValidationInput - isMultiSelect={true} - isRequired={true} - onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}})} - onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], other: operationalScope}})} - label={i18n('Operational Scope')} - validations={{required: true}} - multiSelectedEnum={operationalScope && operationalScope.choices} - otherValue={operationalScope && operationalScope.other} - values={licenseKeyGroupOptionsInputValues.OPERATIONAL_SCOPE}/> - </div> - <div className='license-key-groups-form-row'> - <ValidationInput - onChange={description => onDataChanged({description})} - ref='description' - label={i18n('Description')} - value={description} - validations={{maxLength: 1000, required: true}} - type='textarea'/> - <ValidationInput - isRequired={true} - onEnumChange={type => onDataChanged({type})} - selectedEnum={type} - label={i18n('Type')} - type='select' - validations={{required: true}} - values={licenseKeyGroupOptionsInputValues.TYPE}/> - </div> - </ValidationForm> + <LicenseKeyGroupFormContent + data={data} + onDataChanged={onDataChanged} + genericFieldInfo={genericFieldInfo} + validateName={(value)=> this.validateName(value)} + validateOperationalScope={this.validateOperationalScope}/> + </Form>} + </div> ); } @@ -87,6 +151,37 @@ class LicenseKeyGroupsEditorView extends React.Component { const {data: licenseKeyGroup, previousData: previousLicenseKeyGroup} = this.props; this.props.onSubmit({licenseKeyGroup, previousLicenseKeyGroup}); } + + validateName(value) { + const {data: {id}, LKGNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: LKGNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('License key group by the name \'' + value + '\' already exists. License key group name must be unique')}; + } + + validateOperationalScope(value) { + if (value && value.choices && value.choices.length > 0) { + if (value.choices[0] !== optionInputOther.OTHER) + { + return { + isValid: true, + errorText: '' + }; + } else { + if ( value.other ) { + return { + isValid: true, + errorText: '' + }; + } + } + } + return { + isValid: false, + errorText: 'Field is required' + }; + } } export default LicenseKeyGroupsEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js index e1b610f973..e2c6c30e21 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js @@ -1,27 +1,24 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; -import LicenseKeyGroupsListEditorView from './LicenseKeyGroupsListEditorView.jsx'; +import LicenseKeyGroupsListEditorView, {generateConfirmationMsg} from './LicenseKeyGroupsListEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {actionTypes as globalMoadlActions} from 'nfvo-components/modal/GlobalModalConstants.js'; const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) => { let {licenseKeyGroupsList} = licenseKeyGroup; @@ -38,11 +35,18 @@ const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) }; }; -const mapActionsToProps = (dispatch) => { +const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onAddLicenseKeyGroupClick: () => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch), onEditLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup}), - onDeleteLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseKeyGroup}) + onDeleteLicenseKeyGroupClick: licenseKeyGroup => dispatch({ + type: globalMoadlActions.GLOBAL_MODAL_WARNING, + data:{ + msg: generateConfirmationMsg(licenseKeyGroup), + title: i18n('Warning'), + onConfirmed: ()=>LicenseKeyGroupsActionHelper.deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId:licenseKeyGroup.id, version}) + } + }) }; }; 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 1ed1d2093a..a303e46706 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -8,8 +23,6 @@ import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.js import LicenseKeyGroupsEditor from './LicenseKeyGroupsEditor.js'; import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; import {optionsInputValues} from './LicenseKeyGroupsConstants'; -import LicenseKeyGroupsConfirmationModal from './LicenseKeyGroupsConfirmationModal.jsx'; - class LicenseKeyGroupsListEditorView extends React.Component { static propTypes = { @@ -33,35 +46,33 @@ class LicenseKeyGroupsListEditorView extends React.Component { }; render() { - let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {licenseModelId, vendorName, 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 for {vendorName} License Model', {vendorName})} + title={i18n('License Key Groups', {vendorName})} plusButtonTitle={i18n('Add License Key Group')} onAdd={onAddLicenseKeyGroupClick} filterValue={localFilter} - onFilter={filter => this.setState({localFilter: filter})} + onFilter={value => this.setState({localFilter: value})} isReadOnlyMode={isReadOnlyMode}> {this.filterList().map(licenseKeyGroup => (this.renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode)))} </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='license-key-groups-modal'> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal license-key-groups-modal'> <Modal.Header> <Modal.Title>{`${isModalInEditMode ? i18n('Edit License Key Group') : i18n('Create New License Key Group')}`}</Modal.Title> </Modal.Header> <Modal.Body> { isDisplayModal && ( - <LicenseKeyGroupsEditor licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/> + <LicenseKeyGroupsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/> ) } </Modal.Body> </Modal> - <LicenseKeyGroupsConfirmationModal licenseModelId={licenseModelId}/> - </div> ); } @@ -134,5 +145,17 @@ class LicenseKeyGroupsListEditorView extends React.Component { 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 subMsg = licenseKeyGroupToDelete.referencingFeatureGroups + && licenseKeyGroupToDelete.referencingFeatureGroups.length > 0 ? + i18n('This license key group is associated with one or more feature groups') : + ''; + return ( + <div> + <p>{msg}</p> + <p>{subMsg}</p> + </div> + ); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js index 54ce4e3955..1f0a64e295 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './LicenseKeyGroupsConstants.js'; export default (state = [], action) => { switch (action.type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverview.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverview.js new file mode 100644 index 0000000000..1ca4f37988 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverview.js @@ -0,0 +1,163 @@ +/*! + * 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 LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import LicenseModelOverviewView from './LicenseModelOverviewView.jsx'; +import {overviewEditorHeaders, selectedButton} from './LicenseModelOverviewConstants.js'; +import licenseModelOverviewActionHelper from './licenseModelOverviewActionHelper.js'; + +export const mapStateToProps = ({licenseModel: {licenseModelEditor, entitlementPool, licenseAgreement, featureGroup, licenseKeyGroup, licenseModelOverview}}) => { + + let modalHeader, licensingDataList; + let isDisplayModal = false; + + const reduceLicenseKeyGroups = (accum, licenseKeyGroupId) => { + let curLicenseKeyGroup = licenseKeyGroup.licenseKeyGroupsList.find(item => {return item.id === licenseKeyGroupId;}); + if (curLicenseKeyGroup) { + accum.push({ + ...curLicenseKeyGroup, + itemType: overviewEditorHeaders.LICENSE_KEY_GROUP + }); + } + return accum; + }; + + const reduceEntitlementPools = (accum, entitlementPoolId) => { + let curEntitlementPool = entitlementPool.entitlementPoolsList.find(item => {return item.id === entitlementPoolId;}); + if (curEntitlementPool) { + accum.push ({ + ...curEntitlementPool, + itemType: overviewEditorHeaders.ENTITLEMENT_POOL + }); + } + return accum; + }; + + const reduceFeatureGroups = (accum, featureGroupId) => { + let curFeatureGroup = featureGroup.featureGroupsList.find(item => {return item.id === featureGroupId;}); + if (curFeatureGroup) { + let {entitlementPoolsIds = [], licenseKeyGroupsIds = []} = curFeatureGroup; + accum.push({ + ...curFeatureGroup, + itemType: overviewEditorHeaders.FEATURE_GROUP, + children: [ + ...entitlementPoolsIds.length ? entitlementPoolsIds.reduce(reduceEntitlementPools, []) : [], + ...licenseKeyGroupsIds.length ? licenseKeyGroupsIds.reduce(reduceLicenseKeyGroups, []) : [] + ] + }); + } + return accum; + }; + + + const checkEP = (accum, elem) => { + if (!elem.referencingFeatureGroups || !elem.referencingFeatureGroups.length) { + accum.push({ + ...elem, + itemType: overviewEditorHeaders.ENTITLEMENT_POOL + }); + } + return accum; + }; + + const checkLG = (accum, elem) => { + if (!elem.referencingFeatureGroups || !elem.referencingFeatureGroups.length) { + accum.push({ + ...elem, + itemType: overviewEditorHeaders.LICENSE_KEY_GROUP + }); + } + return accum; + }; + + const checkFG = (accum, elem) => { + if (!elem.referencingLicenseAgreements || !elem.referencingLicenseAgreements.length) { + let {entitlementPoolsIds = [], licenseKeyGroupsIds = []} = elem; + accum.push({ + ...elem, + itemType: overviewEditorHeaders.FEATURE_GROUP, + + children: [ + ...entitlementPoolsIds.length ? entitlementPoolsIds.reduce(reduceEntitlementPools, []) : [], + ...licenseKeyGroupsIds.length ? licenseKeyGroupsIds.reduce(reduceLicenseKeyGroups, []) : [] + ] + + }); + } + return accum; + }; + + + + const mapLicenseAgreementData = licenseAgreement => { + let {featureGroupsIds = []} = licenseAgreement; + return { + ...licenseAgreement, + itemType: overviewEditorHeaders.LICENSE_AGREEMENT, + children: featureGroupsIds.length ? featureGroupsIds.reduce(reduceFeatureGroups, []) : [] + }; + }; + + if (entitlementPool.entitlementPoolEditor && entitlementPool.entitlementPoolEditor.data) { + modalHeader = overviewEditorHeaders.ENTITLEMENT_POOL; + isDisplayModal = true; + }else + if (licenseAgreement.licenseAgreementEditor && licenseAgreement.licenseAgreementEditor.data) { + modalHeader = overviewEditorHeaders.LICENSE_AGREEMENT; + isDisplayModal = true; + }else + if (featureGroup.featureGroupEditor && featureGroup.featureGroupEditor.data) { + modalHeader = overviewEditorHeaders.FEATURE_GROUP; + isDisplayModal = true; + }else + if (licenseKeyGroup.licenseKeyGroupsEditor && licenseKeyGroup.licenseKeyGroupsEditor.data) { + modalHeader = overviewEditorHeaders.LICENSE_KEY_GROUP; + isDisplayModal = true; + } + + if (licenseModelOverview.selectedTab === selectedButton.NOT_IN_USE) { + licensingDataList = [ + ...featureGroup.featureGroupsList.reduce(checkFG, []), + ...entitlementPool.entitlementPoolsList.reduce(checkEP, []), + ...licenseKeyGroup.licenseKeyGroupsList.reduce(checkLG, []) + ]; + }else { + licensingDataList = licenseAgreement.licenseAgreementList && licenseAgreement.licenseAgreementList.length ? licenseAgreement.licenseAgreementList.map(mapLicenseAgreementData) : []; + } + + return { + isReadOnlyMode: VersionControllerUtils.isReadOnly(licenseModelEditor.data), + isDisplayModal, + modalHeader, + licenseModelId: licenseModelEditor.data.id, + version: licenseModelEditor.data.version, + licensingDataList, + selectedTab: licenseModelOverview.selectedTab + + }; +}; + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onCallVCAction: action => { + LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}); + }, + onTabSelect: (buttonTab) => licenseModelOverviewActionHelper.selectVLMListView(dispatch,{buttonTab}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseModelOverviewView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js new file mode 100644 index 0000000000..b5a27ed018 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js @@ -0,0 +1,42 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const overviewItems = keyMirror({ + LICENSE_AGREEMENTS: 'License Agreements', + FEATURE_GROUPS: 'Feature Groups', + ENTITLEMENT_POOLS: 'Entitlement Pools', + LICENSE_KEY_GROUPS: 'License Key Groups' +}); + +export const overviewEditorHeaders = keyMirror({ + LICENSE_AGREEMENT: 'License Agreement', + FEATURE_GROUP: 'Feature Group', + ENTITLEMENT_POOL: 'Entitlement Pool', + LICENSE_KEY_GROUP: 'License Key Group' +}); + +export const actionTypes = keyMirror({ + LICENSE_MODEL_OVERVIEW_TAB_SELECTED: null, + LM_DATA_CHANGED: null +}); + +export const selectedButton = keyMirror({ + VLM_LIST_VIEW: null, + NOT_IN_USE: null +}); + +export const VLM_DESCRIPTION_FORM = 'VLMDEWSCRIPTIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx new file mode 100644 index 0000000000..d6c79ddb52 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx @@ -0,0 +1,105 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import classNames from 'classnames'; + +import EntitlementPoolsEditor from '../entitlementPools/EntitlementPoolsEditor.js'; +import FeatureGroupEditor from '../featureGroups/FeatureGroupEditor.js'; +import LicenseAgreementEditor from '../licenseAgreement/LicenseAgreementEditor.js'; +import LicenseKeyGroupsEditor from '../licenseKeyGroups/LicenseKeyGroupsEditor.js'; +import {overviewEditorHeaders, selectedButton} from './LicenseModelOverviewConstants.js'; + +import SummaryView from './SummaryView.jsx'; +import VLMListView from './VLMListView.jsx'; +import ListButtons from './summary/ListButtons.jsx'; + + +const setModalClassName = (modalHeader) => { + switch (modalHeader) { + case overviewEditorHeaders.ENTITLEMENT_POOL: + return 'entitlement-pools-modal'; + case overviewEditorHeaders.LICENSE_AGREEMENT: + return 'license-agreement-modal'; + case overviewEditorHeaders.FEATURE_GROUP: + return 'feature-group-modal'; + case overviewEditorHeaders.LICENSE_KEY_GROUP: + return 'license-key-groups-modal'; + default: + return ''; + } +}; + +class LicenseModelOverviewView extends React.Component { + + static propTypes = { + isDisplayModal: React.PropTypes.bool, + isReadOnlyMode: React.PropTypes.bool, + licenseModelId: React.PropTypes.string, + licensingDataList: React.PropTypes.array, + modalHeader: React.PropTypes.string, + selectedTab: React.PropTypes.symbol, + onTabSelect: React.PropTypes.func, + onCallVCAction: React.PropTypes.func, + onClose: React.PropTypes.func + }; + + render() { + let {isDisplayModal, modalHeader, licensingDataList, selectedTab, onTabSelect} = this.props; + let selectedInUse = selectedTab !== selectedButton.NOT_IN_USE; + + return( + <div className='license-model-overview'> + <SummaryView/> + <div className={classNames('overview-list-section ', !selectedInUse ? 'overview-list-orphans' : '' )}> + <div className='vlm-list-tab-panel'> + <div className='section-title'>{selectedInUse ? i18n('VLM List View') : i18n('Entities not in Use')}</div> + <ListButtons onTabSelect={onTabSelect} selectedInUse={selectedInUse}/> + </div> + <VLMListView licensingDataList={licensingDataList} showInUse={selectedInUse}/> + </div> + { + isDisplayModal && + <Modal show={isDisplayModal} bsSize='large' animation={true} className={classNames('onborading-modal', setModalClassName(modalHeader))}> + <Modal.Header> + <Modal.Title>{`${i18n('Create New ')}${i18n(modalHeader)}`}</Modal.Title> + </Modal.Header> + <Modal.Body> + {this.renderModalBody(modalHeader)} + </Modal.Body> + </Modal> + } + </div> + ); + } + + renderModalBody(modalHeader) { + let {licenseModelId, version} = this.props; + switch (modalHeader) { + case overviewEditorHeaders.ENTITLEMENT_POOL: + return <EntitlementPoolsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + case overviewEditorHeaders.LICENSE_AGREEMENT: + return <LicenseAgreementEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + case overviewEditorHeaders.FEATURE_GROUP: + return <FeatureGroupEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + case overviewEditorHeaders.LICENSE_KEY_GROUP: + return <LicenseKeyGroupsEditor version={version} licenseModelId={licenseModelId} isReadOnlyMode={false}/>; + } + } +} + +export default LicenseModelOverviewView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/SummaryView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/SummaryView.jsx new file mode 100644 index 0000000000..6fcdb477e6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/SummaryView.jsx @@ -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 React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {default as VendorDataView} from './summary/VendorDataView.js'; +import {default as SummaryCountList} from './summary/SummaryCountList.js'; + +function SummaryView() { + return( + <div className='overview-top-section'> + <div className='overview-title'>{i18n('overview')}</div> + <div className='license-model-overview-top'> + <VendorDataView/> + <SummaryCountList/> + </div> + </div> + ); +} + +export default SummaryView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx new file mode 100644 index 0000000000..119008a849 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.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, {Component} from 'react'; +import {Collapse} from 'react-bootstrap'; +import LicenseAgreement from './listItems/LicenseAgreement.jsx'; +import EntitlementPool from './listItems/EntitlementPool.jsx'; +import FeatureGroup from './listItems/FeatureGroup.jsx'; +import LicenseKeyGroup from './listItems/LicenseKeyGroup.jsx'; +import {overviewEditorHeaders} from './LicenseModelOverviewConstants.js'; + +class VLMListView extends Component { + + static propTypes = { + licensingDataList: React.PropTypes.array, + showInUse: React.PropTypes.bool + }; + + state = { + + }; + + render() { + let {licensingDataList = []} = this.props; + return ( + <div className='vlm-list-view'> + <div> + <ul className='vlm-list' data-test-id='vlm-list'> + {licensingDataList.map(item => this.renderLicensingItem(item))} + </ul> + </div> + </div> + ); + } + + renderLicensingItem(item) { + switch (item.itemType) { + case overviewEditorHeaders.LICENSE_AGREEMENT : + return this.renderLicenseAgreementItem(item); + case overviewEditorHeaders.FEATURE_GROUP : + return this.renderFeatureGroupItem(item); + case overviewEditorHeaders.LICENSE_KEY_GROUP : + return this.renderLicenseKeyGroupItem(item); + case overviewEditorHeaders.ENTITLEMENT_POOL: + return this.renderEntitlementPoolItem(item); + default: + return; + } + } + + renderLicenseAgreementItem(licenseAgreement) { + return ( + <li key={licenseAgreement.id}> + <LicenseAgreement + laData={licenseAgreement} + isCollapsed={this.state[licenseAgreement.id]} + onClick={event => this.updateCollapsable(event, licenseAgreement.id) }/> + <Collapse in={this.state[licenseAgreement.id]}> + <ul> + {licenseAgreement.children && licenseAgreement.children.map(item => this.renderLicensingItem(item))} + </ul> + </Collapse> + </li> + ); + } + + renderFeatureGroupItem(featureGroup) { + const {showInUse} = this.props; + return ( + <li key={featureGroup.id}> + <FeatureGroup + fgData={featureGroup} + isCollapsed={this.state[featureGroup.id]} + onClick={event=> this.updateCollapsable(event, featureGroup.id) }/> + { + showInUse && <Collapse in={this.state[featureGroup.id]}> + <ul> + {featureGroup.children && featureGroup.children.map(item => this.renderLicensingItem(item))} + + </ul> + </Collapse> + } + </li> + ); + } + + renderEntitlementPoolItem(entitlementPool) { + return ( + <li key={entitlementPool.id}> + <EntitlementPool epData={entitlementPool} /> + </li> + ); + } + + renderLicenseKeyGroupItem(licenseKeyGroup) { + return ( + <li key={licenseKeyGroup.id}> + <LicenseKeyGroup lkgData={licenseKeyGroup} /> + </li> + ); + } + + updateCollapsable(event, id) { + event.stopPropagation(); + let obj = {}; + obj[id] = !this.state[id]; + this.setState(obj); + } +} + +export default VLMListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js new file mode 100644 index 0000000000..f0286ba3bb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js @@ -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 {actionTypes} from './LicenseModelOverviewConstants.js'; + +export default { + selectVLMListView(dispatch, {buttonTab}) { + dispatch({ + type: actionTypes.LICENSE_MODEL_OVERVIEW_TAB_SELECTED, + buttonTab + }); + }, + + editDescriptionOpen(dispatch, {description}) { + dispatch({ + type: actionTypes.LM_DATA_CHANGED, + description + }); + }, + + editDescriptionClose(dispatch) { + dispatch({ + type: actionTypes.LM_DATA_CHANGED, + description: false + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx new file mode 100644 index 0000000000..94977b40d1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx @@ -0,0 +1,53 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {extractValue, extractUnits} from '../../entitlementPools/EntitlementPoolsConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; + +class EntitlementPool extends Component { + render() { + let {epData: {name, description, manufacturerReferenceNumber}} = this.props; + return ( + <div className='vlm-list-item vlm-list-item-ep' data-test-id='vlm-list-item-ep'> + <ArrowCol/> + <IconCol className='ep-icon'/> + <ItemInfo name={name} description={description}/> + <AdditionalDataCol> + <AdditionalDataElement + className='vlm-list-item-entitlement-metric' + name={i18n('Entitlement')} + value={this.getEntitlement()}/> + <AdditionalDataElement + name={i18n('Manufacturer Reference Number')} + value={manufacturerReferenceNumber} + className='vlm-list-item-sku'/> + </AdditionalDataCol> + </div> + ); + } + + getEntitlement() { + let {epData: {entitlementMetric, aggregationFunction, time, thresholdValue, thresholdUnits}} = this.props; + return `${extractValue(aggregationFunction)} ${extractValue(entitlementMetric)} per ${extractValue(time)} ${thresholdValue} ${extractUnits(thresholdUnits)}`; + } + +} + +export default EntitlementPool; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx new file mode 100644 index 0000000000..8dbd46a29e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx @@ -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 React, {Component} from 'react'; +import {overviewEditorHeaders} from '../LicenseModelOverviewConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; + +class FeatureGroup extends Component { + render() { + let {fgData: {name, description, children = []}, isCollapsed, onClick} = this.props; + return ( + <div onClick={e => onClick(e)} className='vlm-list-item vlm-list-item-fg' data-test-id='vlm-list-item-fg'> + <ArrowCol isCollapsed={isCollapsed} length={children.length} /> + <IconCol className='fg-icon'/> + <ItemInfo name={name} description={description}> + <div className='children-count'> + <span className='count-value'> + Entitlement Pools: + <span data-test-id='vlm-list-ep-count-value'> + {`${children.filter(child => child.itemType === overviewEditorHeaders.ENTITLEMENT_POOL).length}`} + </span> + </span> + <span className='count-value'> + License Key Groups: + <span data-test-id='vlm-list-lkg-count-value'> + {`${children.filter(child => child.itemType === overviewEditorHeaders.LICENSE_KEY_GROUP).length}`} + </span> + </span> + </div> + </ItemInfo> + </div> + ); + } +} + +export default FeatureGroup; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx new file mode 100644 index 0000000000..dd4686d330 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx @@ -0,0 +1,53 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import {optionsInputValues} from '../../licenseAgreement/LicenseAgreementConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; + +class LicenseAgreement extends Component { + render() { + let {laData: {name, description, licenseTerm, children = []}, isCollapsed, onClick} = this.props; + return ( + <div onClick={e => onClick(e)} className='vlm-list-item vlm-list-item-la' data-test-id='vlm-list-la-item'> + <ArrowCol isCollapsed={isCollapsed} length={children.length} /> + <IconCol className='la-icon'/> + <ItemInfo name={name} description={description}> + <div className='children-count'> + <span className='count-value'>Feature Groups: <span data-test-id='vlm-list-fg-count-value'>{`${children.length}`}</span></span> + </div> + </ItemInfo> + <AdditionalDataCol> + <AdditionalDataElement + name={i18n('License Model Type')} + value={this.extractValue(licenseTerm)}/> + </AdditionalDataCol> + </div> + ); + } + + extractValue(item) { + if (item === undefined) {return '';} //TODO fix it later + + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; + } +} + +export default LicenseAgreement; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx new file mode 100644 index 0000000000..9722b83336 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.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, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {extractValue, getOperationalScopes} from '../../licenseKeyGroups/LicenseKeyGroupsConstants.js'; +import ArrowCol from './listItemsComponents/ArrowCol.jsx'; +import ItemInfo from './listItemsComponents/ItemInfo.jsx'; +import IconCol from './listItemsComponents/IconCol.jsx'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; + +class LicenseKeyGroup extends Component { + render() { + let {lkgData: {name, description, operationalScope, type}} = this.props; + return ( + <div className='vlm-list-item vlm-list-item-lkg' data-test-id='vlm-list-item-lkg'> + <ArrowCol/> + <IconCol className='lkg-icon'/> + <ItemInfo name={name} description={description}/> + <AdditionalDataCol> + <AdditionalDataElement + className='vlm-list-item-operational-scope' + name={i18n('Operational Scope')} + value={operationalScope && getOperationalScopes(operationalScope)}/> + <AdditionalDataElement + className='vlm-list-item-group-type' + name={i18n('Type')} + value={extractValue(type)}/> + </AdditionalDataCol> + </div> + ); + } + +} + +export default LicenseKeyGroup; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/AdditionalDataCol.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/AdditionalDataCol.jsx new file mode 100644 index 0000000000..5b5daafb4f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/AdditionalDataCol.jsx @@ -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 React from 'react'; + +function AdditionalDataCol({children}) { + return ( + <div className='list-item-section list-item-additional-data-col'> + <div className='additional-data-col-border'></div> + <div className='additional-data'> + {children} + </div> + </div> + ); +} + +AdditionalDataCol.propTypes = { + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.node), + React.PropTypes.node + ]) +}; + +function AdditionalDataElement({className, name, value}) { + return ( + <div className={className}> + <span className='additional-data-name'>{name}: </span> + <span className='additional-data-value'>{value}</span> + </div> + ); +} + +AdditionalDataElement.propTypes = { + name: React.PropTypes.string, + value: React.PropTypes.string, + className: React.PropTypes.string +}; + +export {AdditionalDataCol, AdditionalDataElement}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ArrowCol.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ArrowCol.jsx new file mode 100644 index 0000000000..a5eb9d27dd --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ArrowCol.jsx @@ -0,0 +1,35 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import classNames from 'classnames'; + +function ArrowCol ({isCollapsed, length}) { + return ( + <div className='list-item-section list-item-arrow-col'> + <div className={classNames('arrow-icon', {'chevron': length > 0}, + {'down': (length > 0 && isCollapsed)}, + {'right': (length > 0 && (!isCollapsed))})} > + </div> + </div> + ); +} + +ArrowCol.propTypes = { + isCollapsed: React.PropTypes.bool, + length: React.PropTypes.number +}; + +export default ArrowCol; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/IconCol.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/IconCol.jsx new file mode 100644 index 0000000000..7fd7fcb88a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/IconCol.jsx @@ -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 React from 'react'; + +function IconCol({className}) { + return ( + <div className='list-item-section list-item-icon-col'> + <div className={className}></div> + </div> + ); +} + +export default IconCol; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ItemInfo.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ItemInfo.jsx new file mode 100644 index 0000000000..655a0dd4a8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/listItemsComponents/ItemInfo.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'; + +function ItemInfo({name, description, children}) { + return ( + <div className='list-item-section vlm-item-info'> + <div className='vlm-list-item-title'> + <div className='item-name' data-test-id='vlm-list-item-name'>{name}</div> + {children} + </div> + <div className='vlm-list-item-description'>{description}</div> + </div> + ); +} + +ItemInfo.propTypes = { + name: React.PropTypes.string, + description: React.PropTypes.string, + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.node), + React.PropTypes.node + ]) +}; + +export default ItemInfo; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx new file mode 100644 index 0000000000..0c0103fc10 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx @@ -0,0 +1,56 @@ +/*! + * 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 Input from 'nfvo-components/input/validation/Input.jsx'; + +class LicenseModelDescriptionEdit extends React.Component { + render() { + let {onDataChanged, description, genericFieldInfo} = this.props; + let saveButtonClassName = (genericFieldInfo.description.isValid) ? 'description-save' : 'description-save disabled'; + return( + <div className='vendor-description-edit'> + + <Input + onChange={description => onDataChanged({description})} + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + className='description-edit-textarea' + type='textarea'/> + <div className='buttons-row'> + <div className='buttons-wrapper'> + <div onClick={() => this.onClose()} className='description-button' data-test-id='vlm-summary-vendor-desc-cancel-btn'>cancel</div> + <div onClick={() => this.submit()} className={saveButtonClassName} data-test-id='vlm-summary-vendor-desc-save-btn'>save</div> + </div> + </div> + </div> + ); + } + + onClose() { + this.props.onClose(); + } + + submit() { + let {onSubmit, data, description} = this.props; + onSubmit({ + ...data, + description: description + }); + } +} + +export default LicenseModelDescriptionEdit; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx new file mode 100644 index 0000000000..730ccb33f1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.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 {selectedButton} from '../LicenseModelOverviewConstants.js'; + +function ListButtons ({onTabSelect, selectedInUse}) { + return ( + <div className='overview-buttons-section'> + <div onClick={()=>onTabSelect(selectedButton.VLM_LIST_VIEW)} + className={selectedInUse ? 'button-vlm-list-view vlm-list-icon selected' : 'button-vlm-list-view vlm-list-icon' } + data-test-id='vlm-overview-vlmlist-tab'></div> + <div onClick={()=>onTabSelect(selectedButton.NOT_IN_USE)} + className={selectedInUse ? 'button-vlm-list-view entities-list-icon' : 'button-vlm-list-view entities-list-icon selected' } + data-test-id='vlm-overview-orphans-tab' > + </div> + + </div> + ); +} + +ListButtons.propTypes = { + onTabSelect: React.PropTypes.func, + selectedInUse: React.PropTypes.bool +}; + +export default ListButtons; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx new file mode 100644 index 0000000000..66f2cc6838 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx @@ -0,0 +1,31 @@ +/*! + * 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'; + +function SummaryCountItem ({name, counter, onAdd, onNavigate, isReadOnlyMode}) { + return( + <div className='summary-count-item'> + <div className='summary-name-and-count' onClick={onNavigate}> + <span className='item-name' onClick={onNavigate}>{name}</span> + <span className='item-count' onClick={onNavigate} data-test-id={'vlm-summary-vendor-counter-' + name.toLowerCase().replace(/\s/g,'-')}>({counter})</span> + </div> + <div className={isReadOnlyMode ? 'add-button disabled' : 'add-button'} onClick={onAdd} data-test-id={'vlm-summary-vendor-add-btn-' + name.toLowerCase().replace(/\s/g,'-')}/> + </div> + ); +} + +export default SummaryCountItem; + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountList.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountList.js new file mode 100644 index 0000000000..c69a092d23 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountList.js @@ -0,0 +1,126 @@ +/*! + * 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 VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import OnboardingActionHelper from '../../../OnboardingActionHelper.js'; +import EntitlementPoolsActionHelper from '../../entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseAgreementActionHelper from '../../licenseAgreement/LicenseAgreementActionHelper.js'; +import LicenseKeyGroupsActionHelper from '../../licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; +import FeatureGroupsActionHelper from '../../featureGroups/FeatureGroupsActionHelper.js'; + +import {overviewItems} from '../LicenseModelOverviewConstants.js'; +import SummaryCountItem from './SummaryCountItem.jsx'; + +export const mapStateToProps = ({licenseModel: {licenseModelEditor, licenseAgreement: {licenseAgreementList}, + featureGroup: {featureGroupsList}, entitlementPool: {entitlementPoolsList}, licenseKeyGroup: {licenseKeyGroupsList}}}) => { + + let {vendorName, description, id, version} = licenseModelEditor.data; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + let counts = [ + {name: overviewItems.LICENSE_AGREEMENTS, count: licenseAgreementList.length}, + {name: overviewItems.FEATURE_GROUPS, count: featureGroupsList.length}, + {name: overviewItems.ENTITLEMENT_POOLS, count: entitlementPoolsList.length}, + {name: overviewItems.LICENSE_KEY_GROUPS, count: licenseKeyGroupsList.length}, + ]; + return { + vendorName, + licenseModelId: id, + description, + counts, + isReadOnlyMode, + version + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onEditorOpenClick: (name, licenseModelId, version) => { + switch (name) { + case overviewItems.ENTITLEMENT_POOLS: + EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch); + break; + case overviewItems.FEATURE_GROUPS: + FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_AGREEMENTS: + LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_KEY_GROUPS: + LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch); + break; + default: + break; + } + }, + onNavigateClick: ({name, licenseModelId, version}) => { + switch (name) { + case overviewItems.ENTITLEMENT_POOLS: + OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version}); + break; + case overviewItems.FEATURE_GROUPS: + OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_AGREEMENTS: + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version}); + break; + case overviewItems.LICENSE_KEY_GROUPS: + OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}); + break; + default: + break; + } + } + }; +}; + +export class SummaryCountList extends React.Component { + + render() { + let {counts} = this.props; + return( + <div className='summary-count-list'> + {counts.map(item => this.renderItem(item))} + </div> + ); + } + + renderItem(item){ + const {name, count} = item; + const {isReadOnlyMode} = this.props; + return( + <SummaryCountItem isReadOnlyMode={isReadOnlyMode} name={name} counter={count} onNavigate={() => this.onNavigate(name)} onAdd={() => this.onAdd(name)} key={name} /> + ); + } + + onAdd(name) { + let {onEditorOpenClick, licenseModelId, isReadOnlyMode, version} = this.props; + if (!isReadOnlyMode) { + onEditorOpenClick(name, licenseModelId, version); + } + } + + onNavigate(name) { + let {onNavigateClick, licenseModelId, version} = this.props; + onNavigateClick({licenseModelId, name, version}); + } +} + +export default connect(mapStateToProps, mapActionsToProps)(SummaryCountList); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js new file mode 100644 index 0000000000..1d65ab9869 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js @@ -0,0 +1,86 @@ +/*! + * 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 ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import licenseModelOverviewActionHelper from '../licenseModelOverviewActionHelper.js'; +import LicenseModelActionHelper from '../../LicenseModelActionHelper.js'; +import LicenseModelDescriptionEdit from './LicenseModelDescriptionEdit.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {VLM_DESCRIPTION_FORM} from '../LicenseModelOverviewConstants.js'; + +export const mapStateToProps = ({licenseModel: {licenseModelEditor: {data}, licenseModelOverview: {descriptionEditor: {data: descriptionData, genericFieldInfo} }}}) => { + let description = (descriptionData && descriptionData.description) ? descriptionData.description : null; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(data); + return { + data, + description, + genericFieldInfo, + isReadOnlyMode + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: VLM_DESCRIPTION_FORM}), + onCancel: () => licenseModelOverviewActionHelper.editDescriptionClose(dispatch), + onSubmit: (licenseModel) => { + licenseModelOverviewActionHelper.editDescriptionClose(dispatch); + LicenseModelActionHelper.saveLicenseModel(dispatch, {licenseModel}); + }, + onVendorDescriptionEdit: description => licenseModelOverviewActionHelper.editDescriptionOpen(dispatch,{description}) + }; +}; + + + +export class VendorDataView extends React.Component { + render() { + let {data: {vendorName}, description, isReadOnlyMode} = this.props; + return ( + <div className='vendor-data-view'> + <div className='vendor-title'>vendor</div> + <div className='vendor-name' data-test-id='vlm-summary-vendor-name'>{vendorName}</div> + { + description && !isReadOnlyMode ? this.renderDescriptionEdit() : this.renderDescription() + } + </div> + ); + } + + renderDescription() { + let {data: {description}, onVendorDescriptionEdit, isReadOnlyMode} = this.props; + return ( + <div onClick={() => {if (!isReadOnlyMode) {onVendorDescriptionEdit(description);}}} className={!isReadOnlyMode ? 'vendor-description' : 'vendor-description-readonly'}> + <div className='description-data' data-test-id='vlm-summary-vendor-description'> + {description} + </div> + </div> + ); + } + + renderDescriptionEdit() { + let {onCancel, onDataChanged, onSubmit, description, genericFieldInfo, data} = this.props; + return( + <LicenseModelDescriptionEdit onClose={onCancel} onDataChanged={onDataChanged} onSubmit={onSubmit} data={data} genericFieldInfo={genericFieldInfo} description={description}/> + ); + } + +} + +export default connect(mapStateToProps, mapActionsToProps)(VendorDataView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx new file mode 100644 index 0000000000..c63fbff21b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx @@ -0,0 +1,134 @@ +/*! + * 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 {catalogItemTypeClasses, migrationStatusMapper} from './onboardingCatalog/OnboardingCatalogConstants.js'; +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 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> +); + +const ItemTypeTitle = ({catalogItemTypeClass}) => { + const itemTypeTitle = catalogItemTypeClass === catalogItemTypeClasses.LICENSE_MODEL ? i18n('VLM') : i18n('VSP'); + 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 CatalogTileItemName = ({name}) => ( + <div> + <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> + <div className='catalog-tile-item-name'>{name}</div> + </OverlayTrigger> + </div> +); + +const VersionInfo = ({version}) => ( + <div className='catalog-tile-version-info'> + <div className='catalog-tile-item-version' data-test-id='catalog-item-version'> + V {version} + </div> + </div> +); + +const EntityDetails = ({catalogItemData, catalogItemTypeClass}) => { + const {vendorName, name, version} = catalogItemData; + return ( + <div className='catalog-tile-entity-details'> + <CatalogTileVendorName catalogItemTypeClass={catalogItemTypeClass} vendorName={vendorName}/> + <CatalogTileItemName name={name}/> + <VersionInfo version={version.label} /> + </div> + ); +}; + + +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-locking-user-name'>{i18n(status)}</div> + <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> + </div> + + ); +}; + +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 { + 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> + </CatalogTile> + ); + +}; + +CatalogItemDetails.PropTypes = { + catalogItemData: React.PropTypes.obj, + catalogItemTypeClass: React.PropTypes.string, + onSelect: React.PropTypes.func, + onMigrate: React.PropTypes.func +}; + +export default CatalogItemDetails; + diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js new file mode 100644 index 0000000000..c4e2724eaf --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js @@ -0,0 +1,36 @@ +import React from 'react'; +import {storiesOf, action} from '@kadira/storybook'; +import {select, withKnobs} from '@kadira/storybook-addon-knobs'; +import CatalogItemDetails from './CatalogItemDetails.jsx'; +import {FinalizedLicenseModelFactory} from 'test-utils/factories/licenseModel/LicenseModelFactories.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + + + +const stories = storiesOf('CatalogTiles', module); +stories.addDecorator(withKnobs); + +const types = [ + 'license-model-type', + 'software-product-type' +]; + +function selectType() { + return select('Item type' , types, types[0]); +} + +let vlm = FinalizedLicenseModelFactory.build({name: 'Test-VLM'}); +let unclockedVlm = {...vlm, status: statusEnum.CHECK_OUT_STATUS}; + + +stories + .add('preview', () => ( + <div className='catalog-view'> + <div className='catalog-list'> + <div className='catalog-items'> + <CatalogItemDetails catalogItemData={vlm} catalogItemTypeClass={selectType()} onSelect={action('onSelect')} onMigrate={action('onMigrate')}/> + <CatalogItemDetails catalogItemData={unclockedVlm} catalogItemTypeClass={selectType()} onSelect={action('onSelect')} onMigrate={action('onMigrate')}/> + </div> + </div> + </div> + )); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx new file mode 100644 index 0000000000..17248e3b02 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx @@ -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 React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; + +const SoftwareProductListHeader = ({selectedVendor, onBack}) => ( + <div className='vendor-page-header'> + <SVGIcon name='back' onClick={onBack}/> + <div className='tab-separator' /> + <div className='vendor-name'>{selectedVendor.vendorName}</div> + </div> +); + +const CatalogList = ({children, onAddVLM, onAddVSP, vendorPageOptions}) => ( + <div className='catalog-list'> + {vendorPageOptions && <SoftwareProductListHeader onBack={vendorPageOptions.onBack} selectedVendor={vendorPageOptions.selectedVendor}/>} + <div className='catalog-items'> + <div className='create-catalog-item-wrapper'> + {onAddVLM && <CreateItemTile onClick={onAddVLM} dataTestId={'catalog-add-new-lm'} className='vlm-type' title={i18n('CREATE NEW VLM')}/>} + {onAddVSP && + <CreateItemTile onClick={onAddVSP} dataTestId={'catalog-add-new-vsp'} className='vsp-type' title={i18n('CREATE NEW VSP')}/>} + </div> + {children} + </div> + </div> +); + +const CreateItemTile = ({onClick, dataTestId, title, className = ''}) => { + return ( + <div className={'create-catalog-item tile ' + className} onClick={() => onClick()} data-test-id={dataTestId}> + <div className='create-item-plus-icon'><SVGIcon name='plus' /></div> + <div className='create-item-text'>{title}</div> + </div> + ); +}; + +export default CatalogList; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogModal.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogModal.jsx new file mode 100644 index 0000000000..1ef9c82822 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogModal.jsx @@ -0,0 +1,62 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import {modalMapper, catalogItemTypes, catalogItemTypeClasses } from './onboardingCatalog/OnboardingCatalogConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import LicenseModelCreation from '../licenseModel/creation/LicenseModelCreation.js'; +import SoftwareProductCreation from '../softwareProduct/creation/SoftwareProductCreation.js'; + +class CatalogModal extends React.Component{ + + getModalDetails(){ + const {modalToShow} = this.props; + switch (modalToShow) { + case catalogItemTypes.LICENSE_MODEL: + return { + title: i18n('New License Model'), + element: <LicenseModelCreation/> + }; + case catalogItemTypes.SOFTWARE_PRODUCT: + return { + title: i18n('New Software Product'), + element: <SoftwareProductCreation/> + }; + } + } + + render(){ + const {modalToShow} = this.props; + const modalDetails = this.getModalDetails(modalToShow); + + return ( + <Modal + show={Boolean(modalDetails)} + className={`${catalogItemTypeClasses[modalMapper[modalToShow]]}-modal`}> + <Modal.Header> + <Modal.Title>{modalDetails && modalDetails.title}</Modal.Title> + </Modal.Header> + <Modal.Body> + { + modalDetails && modalDetails.element + } + </Modal.Body> + </Modal> + ); + } +} + +export default CatalogModal; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogTile.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogTile.jsx new file mode 100644 index 0000000000..c7720a9d0e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogTile.jsx @@ -0,0 +1,31 @@ +/*! + * 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'; + +const CatalogTile = ({catalogItemTypeClass, onSelect, children}) => { + return( + <div className={`catalog-tile tile ${catalogItemTypeClass}`} onClick={(e) => {e.stopPropagation(); e.preventDefault(); onSelect();}} data-test-id={catalogItemTypeClass}> + {children} + </div> + ); +}; + +CatalogTile.PropTypes = { + catalogItemTypeClass: React.PropTypes.string, + onSelect: React.PropTypes.func +}; + +export default CatalogTile; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx new file mode 100644 index 0000000000..ef54848523 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx @@ -0,0 +1,56 @@ +/*! + * 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 {catalogItemTypes, modalMapper, catalogItemTypeClasses} from './onboardingCatalog/OnboardingCatalogConstants.js'; +import {filterCatalogItemsByType} from './onboardingCatalog/OnboardingCatalogUtils.js'; +import CatalogList from './CatalogList.jsx'; +import CatalogItemDetails from './CatalogItemDetails.jsx'; + +class DetailsCatalogView extends React.Component{ + + static propTypes = { + VLMList: React.PropTypes.array, + VSPList: React.PropTypes.array, + onSelectVLM: React.PropTypes.func.isRequired, + onSelectVSP: React.PropTypes.func.isRequired, + onAddVLM: React.PropTypes.func.isRequired, + onAddVSP: React.PropTypes.func.isRequired, + filter: React.PropTypes.string.isRequired + }; + + renderCatalogItems(items, type, filter, onSelect, onMigrate, tileType){ + return filterCatalogItemsByType(items, type, filter).map(item => + <CatalogItemDetails + key={item.id} + catalogItemData={type === catalogItemTypes.LICENSE_MODEL ? {...item, name: item.vendorName} : item} + catalogItemTypeClass={catalogItemTypeClasses[modalMapper[type]]} + onMigrate={onMigrate} + onSelect={() => onSelect(item)} + tileType={tileType} /> + ); + } + + render() { + let {VLMList, VSPList, onAddVSP, onAddVLM, onSelectVLM, onSelectVSP, filter = '', onMigrate, tileType} = this.props; + return ( + <CatalogList onAddVLM={onAddVLM} onAddVSP={onAddVSP}> + {this.renderCatalogItems(VLMList, catalogItemTypes.LICENSE_MODEL, filter, onSelectVLM, onMigrate, tileType)} + {this.renderCatalogItems(VSPList, catalogItemTypes.SOFTWARE_PRODUCT, filter, onSelectVSP, onMigrate, tileType)} + </CatalogList> + ); + } +} +export default DetailsCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js b/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js new file mode 100644 index 0000000000..b13ccbbba2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js @@ -0,0 +1,90 @@ +/*! + * 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 OnboardView from './OnboardView.jsx'; +import OnboardingActionHelper from '../OnboardingActionHelper.js'; +import OnboardingCatalogActionHelper from './onboardingCatalog/OnboardingCatalogActionHelper.js'; +import OnboardActionHelper from './OnboardActionHelper.js'; +import LicenseModelCreationActionHelper from '../licenseModel/creation/LicenseModelCreationActionHelper.js'; +import SoftwareProductCreationActionHelper from '../softwareProduct/creation/SoftwareProductCreationActionHelper.js'; +import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; + +export const mapStateToProps = ({ + onboard: {onboardingCatalog, activeTab, searchValue}, licenseModelList, finalizedLicenseModelList, softwareProductList, finalizedSoftwareProductList +}) => { + + const reduceLicenseModelList = (accum, vlm)=> { + let currentSoftwareProductList = sortByStringProperty( + finalizedSoftwareProductList + .filter(vsp => vsp.vendorId === vlm.id), + 'name' + ); + accum.push({...vlm, softwareProductList: currentSoftwareProductList}); + return accum; + }; + + finalizedLicenseModelList = sortByStringProperty( + licenseModelList + .filter(vlm => finalizedLicenseModelList.map(finalVlm => finalVlm.id).includes(vlm.id)) + .reduce(reduceLicenseModelList, []), + 'vendorName' + ); + + finalizedSoftwareProductList = sortByStringProperty( + softwareProductList + .filter(vsp => finalizedSoftwareProductList.map(finalVsp => finalVsp.id).includes(vsp.id)), + 'name' + ); + + + let {activeTab: catalogActiveTab, vendorCatalog: {vspOverlay, selectedVendor}} = onboardingCatalog; + + return { + finalizedLicenseModelList, + finalizedSoftwareProductList, + licenseModelList, + softwareProductList, + activeTab, + catalogActiveTab, + searchValue, + vspOverlay, + selectedVendor + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onSelectLicenseModel({id: licenseModelId, version}) { + OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version}); + }, + onSelectSoftwareProduct(softwareProduct) { + let {id: softwareProductId, vendorId: licenseModelId, licensingVersion, version} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version, licenseModelId, licensingVersion}); + }, + onAddSoftwareProductClick: (vendorId) => SoftwareProductCreationActionHelper.open(dispatch, vendorId), + onAddLicenseModelClick: () => LicenseModelCreationActionHelper.open(dispatch), + onVspOverlayChange: (vendor) => OnboardingCatalogActionHelper.changeVspOverlay(dispatch, vendor), + closeVspOverlay: () => OnboardingCatalogActionHelper.closeVspOverlay(dispatch), + onCatalogTabClick: (tab) => OnboardingCatalogActionHelper.changeActiveTab(dispatch, tab), + onTabClick: (tab) => OnboardActionHelper.changeActiveTab(dispatch, tab), + onSearch: (searchValue) => OnboardActionHelper.changeSearchValue(dispatch, searchValue), + onVendorSelect: (vendor) => OnboardingCatalogActionHelper.onVendorSelect(dispatch, {vendor}), + onMigrate: ({softwareProduct}) => OnboardingCatalogActionHelper.onMigrate(dispatch, softwareProduct) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(OnboardView); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js new file mode 100644 index 0000000000..6ebb40878f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.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} from './OnboardConstants.js'; + +const OnboardActionHelper = { + resetOnboardStore(dispatch) { + dispatch({ + type: actionTypes.RESET_ONBOARD_STORE + }); + }, + changeActiveTab(dispatch, activeTab) { + this.clearSearchValue(dispatch); + dispatch({ + type: actionTypes.CHANGE_ACTIVE_ONBOARD_TAB, + activeTab + }); + }, + changeSearchValue(dispatch, searchValue) { + dispatch({ + type: actionTypes.CHANGE_SEARCH_VALUE, + searchValue + }); + }, + clearSearchValue(dispatch) { + dispatch({ + type: actionTypes.CHANGE_SEARCH_VALUE, + searchValue: '' + }); + } +}; + +export default OnboardActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardConstants.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardConstants.js new file mode 100644 index 0000000000..41128bfb11 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardConstants.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 tabsMapping = { + 'WORKSPACE': 1, + 'CATALOG': 2 +}; + +export const actionTypes = keyMirror({ + CHANGE_ACTIVE_ONBOARD_TAB: null, + CHANGE_SEARCH_VALUE: null, + RESET_ONBOARD_STORE: null, + VSP_MIGRATION: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardReducer.js new file mode 100644 index 0000000000..72145015db --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardReducer.js @@ -0,0 +1,31 @@ +/*! + * 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, tabsMapping} from './OnboardConstants.js'; +import {combineReducers} from 'redux'; +import onboardingCatalogReducer from './onboardingCatalog/OnboardingCatalogReducer.js'; + +const onboardReducer = combineReducers({ + onboardingCatalog: onboardingCatalogReducer, + activeTab: (state = tabsMapping.WORKSPACE, action) => action.type === actionTypes.CHANGE_ACTIVE_ONBOARD_TAB ? action.activeTab : state, + searchValue: (state = '', action) => action.type === actionTypes.CHANGE_SEARCH_VALUE ? action.searchValue : state +}); + +export default (state, action) => { + if (action.type === actionTypes.RESET_ONBOARD_STORE) { + state = undefined; + } + return onboardReducer(state, action); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx new file mode 100644 index 0000000000..b7a7fa5f68 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx @@ -0,0 +1,95 @@ +/*! + * 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 OnboardingCatalogView from './onboardingCatalog/OnboardingCatalogView.jsx'; +import WorkspaceView from './workspace/WorkspaceView.jsx'; +import {tabsMapping} from './OnboardConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classnames from 'classnames'; +import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; +import objectValues from 'lodash/values.js'; +import {catalogItemTypes} from './onboardingCatalog/OnboardingCatalogConstants.js'; + +const OnboardHeaderTabs = ({onTabClick, activeTab}) => ( + <div className='onboard-header-tabs'> + <div + className={classnames('onboard-header-tab', {'active': activeTab === tabsMapping.WORKSPACE })} + onClick={() => onTabClick(tabsMapping.WORKSPACE)} + data-test-id='onboard-workspace-tab'> + {i18n('WORKSPACE')} + </div> + <div + className={classnames('onboard-header-tab', {'active': activeTab === tabsMapping.CATALOG })} + onClick={() => onTabClick(tabsMapping.CATALOG)} + data-test-id='onboard-onboard-tab'> + {i18n('ONBOARD CATALOG')} + </div> + </div> +); + +const OnboardHeader = ({onSearch, activeTab, onTabClick, searchValue}) => ( + <div className='onboard-header'> + <OnboardHeaderTabs activeTab={activeTab} onTabClick={onTabClick} /> + <ExpandableInput + onChange={onSearch} + iconType='search' + value={searchValue}/> + </div> +); + +class OnboardView extends React.Component { + static propTypes = { + licenseModelList: React.PropTypes.array, + softwareProductList: React.PropTypes.array, + finalizedLicenseModelList: React.PropTypes.array, + finalizedSoftwareProductList: React.PropTypes.array, + modalToShow: React.PropTypes.oneOf(objectValues(catalogItemTypes)), + onSelectLicenseModel: React.PropTypes.func.isRequired, + onSelectSoftwareProduct: React.PropTypes.func.isRequired, + onAddLicenseModelClick: React.PropTypes.func.isRequired, + onAddSoftwareProductClick: React.PropTypes.func.isRequired, + closeVspOverlay: React.PropTypes.func.isRequired, + onVspOverlayChange: React.PropTypes.func.isRequired, + onTabClick: React.PropTypes.func.isRequired, + onCatalogTabClick: React.PropTypes.func.isRequired, + onSearch: React.PropTypes.func.isRequired, + activeTab: React.PropTypes.number.isRequired, + catalogActiveTab: React.PropTypes.number.isRequired, + searchValue: React.PropTypes.string.isRequired, + onMigrate: React.PropTypes.func.isRequired, + }; + renderViewByTab(activeTab){ + switch (activeTab){ + case tabsMapping.WORKSPACE: + return <WorkspaceView {...this.props} />; + case tabsMapping.CATALOG: + default: + return <OnboardingCatalogView {...this.props} />; + } + } + + render() { + let {closeVspOverlay, activeTab, onTabClick, onSearch, searchValue} = this.props; + return ( + <div className='catalog-view' onClick={closeVspOverlay}> + <OnboardHeader activeTab={activeTab} onTabClick={onTabClick} searchValue={searchValue} onSearch={value => onSearch(value)}/> + {this.renderViewByTab(activeTab)} + </div> + ); + } +} + +export default OnboardView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js new file mode 100644 index 0000000000..73a447558d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogActionHelper.js @@ -0,0 +1,85 @@ +/*! + * 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 {actionTypes} from './OnboardingCatalogConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import OnboardActionHelper from '../OnboardActionHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; + + +function getMessageForMigration(name) { + return ( + <div> + <div>{i18n('{name} needs to be updated. Click ‘Checkout & Update’, to proceed.',{name})}</div> + <div>{i18n('Please don’t forget to submit afterwards')}</div> + </div> + ); +} + +const OnboardingCatalogActionHelper = { + changeVspOverlay(dispatch, vendor) { + dispatch({ + type: actionTypes.CHANGE_VSP_OVERLAY, + vendorId: vendor ? vendor.id : null + }); + }, + closeVspOverlay(dispatch) { + dispatch({ + type: actionTypes.CLOSE_VSP_OVERLAY + }); + }, + changeActiveTab(dispatch, activeTab) { + OnboardActionHelper.clearSearchValue(dispatch); + dispatch({ + type: actionTypes.CHANGE_ACTIVE_CATALOG_TAB, + activeTab + }); + }, + onVendorSelect(dispatch, {vendor}) { + OnboardActionHelper.clearSearchValue(dispatch); + dispatch({ + type: actionTypes.ONBOARDING_CATALOG_OPEN_VENDOR_PAGE, + selectedVendor: vendor + }); + }, + onMigrate(dispatch, softwareProduct) { + const {status, name, lockingUser} = softwareProduct; + if (status === statusEnum.CHECK_OUT_STATUS && !VersionControllerUtils.isCheckedOutByCurrentUser(softwareProduct)) { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data: { + title: 'WARNING', + msg: i18n('{name} is locked by user {lockingUser} for self-healing',{name, lockingUser}) + } + }); + } else { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + title: 'WARNING', + msg: getMessageForMigration(softwareProduct.name.toUpperCase()), + confirmationButtonText: i18n('Checkout & Update'), + onConfirmed: ()=>SoftwareProductActionHelper.migrateSoftwareProduct(dispatch, {softwareProduct}) + } + }); + } + } +}; + +export default OnboardingCatalogActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js new file mode 100644 index 0000000000..071160c4fd --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.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 keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const catalogItemTypes = Object.freeze({ + LICENSE_MODEL: 'license-model', + SOFTWARE_PRODUCT: 'software-product' +}); + +export const catalogItemTypeClasses = { + LICENSE_MODEL: 'license-model-type', + SOFTWARE_PRODUCT: 'software-product-type', + VENDOR: 'vendor-type' + +}; + +export const modalMapper = { + 'license-model': 'LICENSE_MODEL', + 'software-product': 'SOFTWARE_PRODUCT' +}; + +export const tabsMapping = { + 'BY_VENDOR': 1, + 'ALL': 2 +}; + +export const migrationStatusMapper = { + OLD_VERSION: 'True', +}; + +export const actionTypes = keyMirror({ + ONBOARDING_CATALOG_OPEN_VENDOR_PAGE: null, + CHANGE_ACTIVE_CATALOG_TAB: null, + CHANGE_SEARCH_VALUE: null, + CHANGE_VSP_OVERLAY: null, + CLOSE_VSP_OVERLAY: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogReducer.js new file mode 100644 index 0000000000..d7d9d0bd6c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogReducer.js @@ -0,0 +1,30 @@ +/*! + * 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, tabsMapping} from './OnboardingCatalogConstants.js'; +import {combineReducers} from 'redux'; +import vendorCatalogReducer from './VendorCatalogReducer.js'; + +const onboardingCatalogReducer = combineReducers({ + vendorCatalog: vendorCatalogReducer, + activeTab: (state = tabsMapping.ALL, action) => action.type === actionTypes.CHANGE_ACTIVE_CATALOG_TAB ? action.activeTab : state +}); + +export default (state, action) => { + if (action.type === actionTypes.RESET_ONBOARDING_CATALOG_STORE) { + state = undefined; + } + return onboardingCatalogReducer(state, action); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogUtils.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogUtils.js new file mode 100644 index 0000000000..ac623db920 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogUtils.js @@ -0,0 +1,21 @@ +/*! + * 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 {catalogItemTypes} from './OnboardingCatalogConstants.js'; + +export const filterCatalogItemsByType = (items, type, filter) => { + const fieldName = type === catalogItemTypes.LICENSE_MODEL ? 'vendorName' : 'name'; + return items.filter(item => item[fieldName].toLowerCase().indexOf(filter.toLowerCase()) > -1); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx new file mode 100644 index 0000000000..b1f002d2fb --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx @@ -0,0 +1,98 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classnames from 'classnames'; +import DetailsCatalogView from 'sdc-app/onboarding/onboard/DetailsCatalogView.jsx'; +import VendorCatalogView from './VendorCatalogView.jsx'; +import { tabsMapping} from './OnboardingCatalogConstants.js'; + +const CatalogHeaderTabs = ({onTabPress, activeTab}) => ( + <div className='catalog-header-tabs'> + <div + className={classnames('catalog-header-tab', {'active': activeTab === tabsMapping.ALL })} + onClick={() => onTabPress(tabsMapping.ALL)} + data-test-id='catalog-all-tab'> + {i18n('ALL')} + </div> + <div className='tab-separator'/> + <div + className={classnames('catalog-header-tab', {'active': activeTab === tabsMapping.BY_VENDOR })} + onClick={() => onTabPress(tabsMapping.BY_VENDOR)} + data-test-id='catalog-by-vendor-tab'> + {i18n('BY VENDOR')} + </div> + </div> +); + +const CatalogHeader = ({activeTab, onTabPress}) => ( + <div className='catalog-header'> + <CatalogHeaderTabs activeTab={activeTab} onTabPress={onTabPress} /> + </div> +); + +class OnboardingCatalogView extends React.Component { + renderViewByTab(activeTab){ + const {finalizedLicenseModelList: licenseModelList, vspOverlay, finalizedSoftwareProductList: softwareProductList, onSelectLicenseModel, onSelectSoftwareProduct, + onAddLicenseModelClick, onAddSoftwareProductClick, onVspOverlayChange, onVendorSelect, selectedVendor, searchValue, onMigrate} = this.props; + switch (activeTab){ + case tabsMapping.ALL: + return ( + <DetailsCatalogView + VLMList={licenseModelList} + VSPList={softwareProductList} + onAddVLM={onAddLicenseModelClick} + onAddVSP={onAddSoftwareProductClick} + onSelectVLM={onSelectLicenseModel} + onSelectVSP={onSelectSoftwareProduct} + filter={searchValue} + onMigrate={onMigrate}/> + ); + case tabsMapping.BY_VENDOR: + default: + return ( + <VendorCatalogView + licenseModelList={licenseModelList} + onAddVSP={onAddSoftwareProductClick} + onAddVLM={onAddLicenseModelClick} + onSelectVSP={onSelectSoftwareProduct} + onSelectVLM={onSelectLicenseModel} + vspOverlay={vspOverlay} + onVendorSelect={onVendorSelect} + selectedVendor={selectedVendor} + onVspOverlayChange={onVspOverlayChange} + onMigrate={onMigrate} + filter={searchValue}/> + ); + } + } + + render() { + const {selectedVendor, catalogActiveTab: activeTab, onCatalogTabClick, onSearch, searchValue} = this.props; + return ( + <div className='catalog-wrapper'> + {!selectedVendor && <CatalogHeader + onSearch={event => onSearch(event.target.value)} + activeTab={activeTab} + onTabPress={tab => onCatalogTabClick(tab)} + searchValue={searchValue}/>} + {this.renderViewByTab(activeTab)} + </div> + ); + } +} + +export default OnboardingCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/Tooltip.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/Tooltip.jsx new file mode 100644 index 0000000000..8d8d1162a0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/Tooltip.jsx @@ -0,0 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +export default function tooltip (msg) { + return ( + <Tooltip className='tile-tooltip' id='tile-tooltip'>{msg}</Tooltip> + ); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VSPOverlay.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VSPOverlay.jsx new file mode 100644 index 0000000000..1ba4834fa3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VSPOverlay.jsx @@ -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 React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {migrationStatusMapper} from './OnboardingCatalogConstants.js'; + +const VSPOverlay = ({VSPList, onSelectVSP, onSeeMore, onMigrate}) => ( + <div className='vsp-overlay-wrapper' onClick={(e) => { + e.stopPropagation(); + e.preventDefault(); + }}> + <div className='vsp-overlay-arrow'></div> + <div className='vsp-overlay'> + <div className='vsp-overlay-title'>{i18n('Recently Edited')}</div> + <div className='vsp-overlay-list'> + {VSPList.slice(0, 5).map(vsp => <div key={vsp.id} className='vsp-overlay-detail' onClick={() => { + if (vsp.isOldVersion && vsp.isOldVersion === migrationStatusMapper.OLD_VERSION) { + onMigrate({ + softwareProduct: vsp + }); + } else { + onSelectVSP(vsp); + } + } + }>{i18n(vsp.name)}</div>)} + </div> + {VSPList.length > 5 && <div className='vsp-overlay-see-more' onClick={onSeeMore}>{i18n('See More')}</div>} + </div> + </div> +); + +VSPOverlay.PropTypes = { + VSPList: React.PropTypes.array, + onSelectVSP: React.PropTypes.func +}; + +export default VSPOverlay; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogReducer.js new file mode 100644 index 0000000000..dd8b41bd22 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogReducer.js @@ -0,0 +1,38 @@ +/*! + * 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 './OnboardingCatalogConstants.js'; + +export default (state = {}, action) => { + switch(action.type) { + case actionTypes.ONBOARDING_CATALOG_OPEN_VENDOR_PAGE: + return { + ...state, + selectedVendor: action.selectedVendor + }; + case actionTypes.CHANGE_VSP_OVERLAY: + return { + ...state, + vspOverlay: action.vendorId + }; + case actionTypes.CLOSE_VSP_OVERLAY: + return { + ...state, + vspOverlay: null + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogView.jsx new file mode 100644 index 0000000000..c4e0599d85 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorCatalogView.jsx @@ -0,0 +1,74 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import VendorItem from './VendorItem.jsx'; +import CatalogList from '../CatalogList.jsx'; +import CatalogItemDetails from '../CatalogItemDetails.jsx'; +import {catalogItemTypes, catalogItemTypeClasses} from './OnboardingCatalogConstants.js'; +import {filterCatalogItemsByType} from './OnboardingCatalogUtils.js'; + +const VendorList = ({onAddVLM, onAddVSP, onSelectVSP, licenseModelList = [], vspOverlay: currentOverlay, onVspOverlayChange, onVendorSelect, filter, onMigrate}) => { + return( + <CatalogList onAddVLM={onAddVLM} onAddVSP={onAddVSP}> + { + filterCatalogItemsByType(licenseModelList, catalogItemTypes.LICENSE_MODEL, filter).map(vlm => + <VendorItem + key={vlm.id} + onAddVSP={onAddVSP} + onSelectVSP={onSelectVSP} + shouldShowOverlay={currentOverlay === vlm.id} + onVSPIconClick={(hasVSP) => onVspOverlayChange(vlm.id === currentOverlay || !hasVSP ? null : vlm)} + onVendorSelect={onVendorSelect} + onMigrate={onMigrate} + vendor={vlm}/>) + } + </CatalogList> + ); +}; + +const SoftwareProductListByVendor = ({onAddVSP, selectedVendor, onVendorSelect, onSelectVSP, onSelectVLM, filter, onMigrate}) => { + return( + <div> + <CatalogList onAddVSP={()=>{onAddVSP(selectedVendor.id);}} vendorPageOptions={{selectedVendor, onBack: () => onVendorSelect(false)}}> + <CatalogItemDetails + key={selectedVendor.id} + onSelect={() => onSelectVLM(selectedVendor)} + catalogItemTypeClass={catalogItemTypeClasses.LICENSE_MODEL} + onMigrate={onMigrate} + catalogItemData={{...selectedVendor, name: selectedVendor.vendorName}}/> + { + filterCatalogItemsByType(selectedVendor.softwareProductList, catalogItemTypes.SOFTWARE_PRODUCT, filter).map(vsp => + <CatalogItemDetails + key={vsp.id} + catalogItemTypeClass={catalogItemTypeClasses.SOFTWARE_PRODUCT} + onMigrate={onMigrate} + onSelect={() => onSelectVSP(vsp)} + catalogItemData={vsp}/> + ) + } + </CatalogList> + </div> + ); +}; + +class VendorCatalogView extends React.Component { + render() { + let {selectedVendor} = this.props; + return( selectedVendor ? <SoftwareProductListByVendor {...this.props}/> : <VendorList {...this.props}/>); + } +} + +export default VendorCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx new file mode 100644 index 0000000000..cecccdd9ad --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.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 {catalogItemTypeClasses} from './OnboardingCatalogConstants.js'; +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 OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import tooltip from './Tooltip.jsx'; + + +class VendorItem extends React.Component { + + static PropTypes = { + softwareProductList: React.PropTypes.array, + vendor: React.PropTypes.object, + onSelectVSP: React.PropTypes.func, + shouldShowOverlay: React.PropTypes.boolm, + onVendorSelect: React.PropTypes.func, + onAddVSP: React.PropTypes.func, + onVSPIconClick: React.PropTypes.func, + + }; + + render() { + let {vendor, onSelectVSP, shouldShowOverlay, onVendorSelect, onMigrate} = this.props; + let {softwareProductList = [], vendorName} = vendor; + return ( + <CatalogTile + catalogItemTypeClass={catalogItemTypeClasses.VENDOR} + onSelect={() => onVendorSelect(vendor)}> + <div className='catalog-tile-top'> + <div className='catalog-tile-icon vendor-type'> + <div className='icon'><SVGIcon name='vendor'/></div> + </div> + <OverlayTrigger placement='top' overlay={tooltip(vendorName)}> + <div className='catalog-tile-item-name'>{vendorName}</div> + </OverlayTrigger> + <div + className={classnames('catalog-tile-vsp-count', {active: shouldShowOverlay}, {clickable: softwareProductList.length})} + onClick={(event) => this.handleVspCountClick(event)} + data-test-id='catalog-vsp-count'> + {i18n(`${softwareProductList.length} VSPs`)} + </div> + <div className='catalog-tile-content' onClick={(event) => this.onCreateVspClick(event)} data-test-id='catalog-create-new-vsp-from-vendor'> + <div className='create-new-vsp-button'> + <SVGIcon name='plus'/> {i18n('Create new VSP')} + </div> + </div> + </div> + + {shouldShowOverlay && softwareProductList.length > 0 + && <VSPOverlay onMigrate={onMigrate} VSPList={softwareProductList} onSelectVSP={onSelectVSP} onSeeMore={() => onVendorSelect(vendor)}/>} + </CatalogTile> + ); + } + + onClick(vlm) { + this.setState({ + licenseModelToShow: vlm + }); + } + + onCreateVspClick(event) { + let {onAddVSP, vendor: {id}} = this.props; + event.stopPropagation(); + event.preventDefault(); + onAddVSP(id); + } + + handleVspCountClick(e){ + let {onVSPIconClick, vendor: {softwareProductList}} = this.props; + e.stopPropagation(); + e.preventDefault(); + const hasVSP = Boolean(softwareProductList.length); + onVSPIconClick(hasVSP); + } + +} + +export default VendorItem; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceView.jsx new file mode 100644 index 0000000000..d86b674f13 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceView.jsx @@ -0,0 +1,57 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import DetailsCatalogView from '../DetailsCatalogView.jsx'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {tabsMapping} from 'sdc-app/onboarding/onboard/OnboardConstants.js'; + +const WorkspaceView = (props) => { + let { + licenseModelList, softwareProductList, onAddLicenseModelClick, + onAddSoftwareProductClick, onSelectLicenseModel, onSelectSoftwareProduct, searchValue, onMigrate + } = props; + + let {getCheckOutStatusKindByUserID} = VersionControllerUtils; + let unfinalizedLicenseModelList = licenseModelList.filter(vlm => { + let {status} = getCheckOutStatusKindByUserID(vlm.status, vlm.lockingUser); + return status !== statusEnum.SUBMIT_STATUS && status !== statusEnum.LOCK_STATUS; + }); + let unfinalizedSoftwareProductList = softwareProductList.filter(vsp =>{ + let {status} = getCheckOutStatusKindByUserID(vsp.status, vsp.lockingUser); + return status !== statusEnum.SUBMIT_STATUS && status !== statusEnum.LOCK_STATUS; + }); + + return ( + <div className='catalog-wrapper workspace-view'> + <div className='catalog-header workspace-header'> + {i18n('WORKSPACE')} + </div> + <DetailsCatalogView + VLMList={unfinalizedLicenseModelList} + VSPList={unfinalizedSoftwareProductList} + onAddVLM={onAddLicenseModelClick} + onAddVSP={onAddSoftwareProductClick} + onSelectVLM={onSelectLicenseModel} + onSelectVSP={onSelectSoftwareProduct} + onMigrate={onMigrate} + filter={searchValue} + tileType={tabsMapping.WORKSPACE} /> + </div>); +}; + +export default WorkspaceView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/FinalizedSoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/FinalizedSoftwareProductReducer.js new file mode 100644 index 0000000000..396f65f5d7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/FinalizedSoftwareProductReducer.js @@ -0,0 +1,25 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './SoftwareProductConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED: + return [...action.response.results]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js index 2dbef6baf2..12f68a2afe 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -1,36 +1,40 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; -import {navigationItems} from './SoftwareProductConstants.js'; +import {navigationItems, mapScreenToNavigationItem} from './SoftwareProductConstants.js'; import SoftwareProductActionHelper from './SoftwareProductActionHelper.js'; import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductDependenciesActionHelper from './dependencies/SoftwareProductDependenciesActionHelper.js'; +import {doesHeatDataExist} from './attachments/SoftwareProductAttachmentsUtils.js'; + +import HeatSetupActionHelper from './attachments/setup/HeatSetupActionHelper.js'; +import { actionsEnum as versionControllerActions } from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +function getActiveNavigationId(screen, componentId) { + let activeItemId = componentId ? mapScreenToNavigationItem[screen] + '|' + componentId : mapScreenToNavigationItem[screen]; + return activeItemId; +} const buildComponentNavigationBarGroups = ({componentId, meta}) => { const groups = ([ @@ -106,6 +110,18 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co id: navigationItems.ATTACHMENTS, name: i18n('Attachments'), disabled: false, + hidden: !doesHeatDataExist(meta.heatSetup), + meta + }, { + id: navigationItems.ACTIVITY_LOG, + name: i18n('Activity Log'), + disabled: false, + meta + }, { + id: navigationItems.DEPENDENCIES, + name: i18n('Component Dependencies'), + hidden: componentsList.length <= 1, + disabled: false, meta }, { id: navigationItems.COMPONENTS, @@ -125,29 +141,7 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co } ] }]; - let activeItemId = ({ - [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: navigationItems.VENDOR_SOFTWARE_PRODUCT, - [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: navigationItems.GENERAL, - [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: navigationItems.ATTACHMENTS, - [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: navigationItems.PROCESS_DETAILS, - [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: navigationItems.COMPONENTS - })[screen]; - - if(componentId) { - activeItemId = - Object.keys(mapOfExpandedIds).length === 1 && mapOfExpandedIds[navigationItems.COMPONENTS] === true ? - navigationItems.COMPONENTS : ({ - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: navigationItems.GENERAL, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: navigationItems.COMPUTE, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: navigationItems.LOAD_BALANCING, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: navigationItems.NETWORKS, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS, - [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING - })[screen] + '|' + componentId; - } - + let activeItemId = getActiveNavigationId(screen, componentId); return { activeItemId, groups }; @@ -158,9 +152,7 @@ const buildVersionControllerProps = (softwareProduct) => { const {data: currentSoftwareProduct = {}, isValidityData = true} = softwareProductEditor; const {version, viewableVersions, status: currentStatus, lockingUser} = currentSoftwareProduct; - const {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ? - VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) : - {status: currentStatus, isCheckedOut: false}; + const {status, isCheckedOut} = VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser); return { status, isCheckedOut, version, viewableVersions, @@ -168,42 +160,56 @@ const buildVersionControllerProps = (softwareProduct) => { }; }; -const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { - const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire} = softwareProduct; - const {data: currentSoftwareProduct = {}, mapOfExpandedIds = []} = softwareProductEditor; +function buildMeta({softwareProduct, componentId, softwareProductDependencies}) { + const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire, softwareProductAttachments} = softwareProduct; + const {data: currentSoftwareProduct = {}} = softwareProductEditor; const {version} = currentSoftwareProduct; - const {componentsList = []} = softwareProductComponents; const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); const {qdata} = softwareProductQuestionnaire; + const {heatSetup, heatSetupCache} = softwareProductAttachments; let currentComponentMeta = {}; if(componentId) { const {componentEditor: {data: componentData = {} , qdata: componentQdata}} = softwareProductComponents; currentComponentMeta = {componentData, componentQdata}; } - const meta = {softwareProduct: currentSoftwareProduct, qdata, version, isReadOnlyMode, currentComponentMeta}; + const meta = {softwareProduct: currentSoftwareProduct, qdata, version, heatSetup, heatSetupCache, isReadOnlyMode, currentComponentMeta, softwareProductDependencies}; + return meta; +} + +const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { + const {softwareProductEditor, softwareProductComponents, softwareProductDependencies} = softwareProduct; + const {mapOfExpandedIds = []} = softwareProductEditor; + const {componentsList = []} = softwareProductComponents; + const meta = buildMeta({softwareProduct, componentId, softwareProductDependencies}); return { versionControllerProps: buildVersionControllerProps(softwareProduct), - navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}), + meta }; }; -const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, meta: {isReadOnlyMode, softwareProduct, qdata, currentComponentMeta: {componentData, componentQdata}}}) => { +const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, + meta: {isReadOnlyMode, softwareProduct, version, qdata, softwareProductDependencies, + currentComponentMeta: {componentData, componentQdata}}}) => { let promise; if (isReadOnlyMode) { promise = Promise.resolve(); } else { switch(screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + promise = SoftwareProductDependenciesActionHelper.saveDependencies(dispatch,{softwareProductId, version, dependenciesList: softwareProductDependencies}); case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: promise = SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata}); break; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: - promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, componentData, qdata: componentQdata}); + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + {softwareProductId, version, vspComponentId: componentId, componentData, qdata: componentQdata}); break; case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: - promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata: componentQdata}); + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata: componentQdata}); break; default: promise = Promise.resolve(); @@ -228,22 +234,22 @@ const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentC OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.COMPUTE: - OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.LOAD_BALANCING: - OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.NETWORKS: OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.STORAGE: - OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.PROCESS_DETAILS: - OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; case navigationItems.MONITORING: - OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId}); + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId, version}); break; } }; @@ -251,44 +257,49 @@ const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentC const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwareProductId, componentId: currentComponentId}}}) => { const props = { - onClose: ({version}) => { - if (screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE) { - OnboardingActionHelper.navigateToOnboardingCatalog(dispatch); - } else { - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); - } - }, - onVersionSwitching: (version) => { - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); + onVersionSwitching: (version, meta) => { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); + props.onNavigate({id: getActiveNavigationId(screen, currentComponentId), meta, version}); }, onToggle: (groups, itemIdToExpand) => groups.map(({items}) => SoftwareProductActionHelper.toggleNavigationItems(dispatch, {items, itemIdToExpand})), - onNavigate: ({id, meta}) => { - let preNavigate = autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}); - preNavigate.then(() => { + onNavigate: ({id, meta, version}) => { + let {heatSetup, heatSetupCache} = meta; + let heatSetupPopupPromise = screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS ? + HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : + Promise.resolve(); + let preNavigate = meta ? autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}) : Promise.resolve(); + version = version || (meta ? meta.version : undefined); + Promise.all([preNavigate, heatSetupPopupPromise]).then(() => { switch(id) { case navigationItems.VENDOR_SOFTWARE_PRODUCT: - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: meta.version}); + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); break; case navigationItems.GENERAL: - OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId, version}); break; case navigationItems.PROCESS_DETAILS: - OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: meta.version}); + OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}); break; case navigationItems.NETWORKS: - OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: meta.version}); + OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version}); + break; + case navigationItems.DEPENDENCIES: + OnboardingActionHelper.navigateToSoftwareProductDependencies(dispatch, {softwareProductId, version}); break; case navigationItems.ATTACHMENTS: - OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}); break; case navigationItems.COMPONENTS: - OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId, version}); + break; + case navigationItems.ACTIVITY_LOG: + OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version}); break; default: - onComponentNavigate(dispatch, {id, softwareProductId, version: meta.version, screen, currentComponentId}); + onComponentNavigate(dispatch, {id, softwareProductId, version, screen, currentComponentId}); break; } - }); + }).catch(() => {}); } }; @@ -297,6 +308,8 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES: + case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: @@ -310,11 +323,20 @@ const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwarePr } - props.onVersionControllerAction = (action) => - SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action}).then(() => { - SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); - }); - + props.onVersionControllerAction = (action, version, meta) => { + let {heatSetup, heatSetupCache} = meta; + let heatSetupPopupPromise = screen === enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS && action === versionControllerActions.CHECK_IN ? + HeatSetupActionHelper.heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) : + Promise.resolve(); + heatSetupPopupPromise.then(() => { + return SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action, version}).then(({newVersion}) => { + //props.onNavigate({id: getActiveNavigationId(screen, currentComponentId), version}); + if(screen === enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG) { + OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {softwareProductId, version: newVersion}); + } + }); + }).catch(() => {}); + }; return props; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js index d9ed8af679..6f53886350 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -26,10 +21,17 @@ import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licens import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; import {actionTypes} from './SoftwareProductConstants.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import {actionTypes as HeatSetupActions} from 'sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js'; +import {actionTypes as featureGroupsActionConstants} from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js'; +import {actionTypes as licenseAgreementActionTypes} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -40,46 +42,58 @@ function softwareProductCategoriesUrl() { return `${restATTPrefix}/v1/categories/resources/`; } -function uploadFile(vspId, formData) { - - return RestAPIUtil.create(`${baseUrl()}${vspId}/upload`, formData); +function uploadFile(vspId, formData, version) { + return RestAPIUtil.post(`${baseUrl()}${vspId}/versions/${version.id}/orchestration-template-candidate`, formData); } -function putSoftwareProduct(softwareData) { - return RestAPIUtil.save(`${baseUrl()}${softwareData.id}`, { - name: softwareData.name, - description: softwareData.description, - category: softwareData.category, - subCategory: softwareData.subCategory, - vendorId: softwareData.vendorId, - vendorName: softwareData.vendorName, - licensingVersion: softwareData.licensingVersion, - icon: softwareData.icon, - licensingData: softwareData.licensingData +function putSoftwareProduct(softwareProduct) { + return RestAPIUtil.put(`${baseUrl()}${softwareProduct.id}/versions/${softwareProduct.version.id}`, { + name: softwareProduct.name, + description: softwareProduct.description, + category: softwareProduct.category, + subCategory: softwareProduct.subCategory, + vendorId: softwareProduct.vendorId, + vendorName: softwareProduct.vendorName, + licensingVersion: softwareProduct.licensingVersion && softwareProduct.licensingVersion.id ? softwareProduct.licensingVersion : {} , + icon: softwareProduct.icon, + licensingData: softwareProduct.licensingData }); } -function putSoftwareProductQuestionnaire(vspId, qdata) { - return RestAPIUtil.save(`${baseUrl()}${vspId}/questionnaire`, qdata); +function putSoftwareProductQuestionnaire(vspId, qdata, version) { + return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata); } -function putSoftwareProductAction(id, action) { - return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +function putSoftwareProductAction(id, action, version) { + return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action}); } function fetchSoftwareProductList() { return RestAPIUtil.fetch(baseUrl()); } +function fetchFinalizedSoftwareProductList() { + return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`); +} + function fetchSoftwareProduct(vspId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl()}${vspId}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`); } function fetchSoftwareProductQuestionnaire(vspId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl()}${vspId}/questionnaire${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`); +} + +function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) { + return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate); +} +function validateHeatCandidate(softwareProductId, version) { + return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`); +} + +function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) { + return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'}); } function objToString(obj) { @@ -88,7 +102,8 @@ function objToString(obj) { obj.forEach((item) => { str += objToString(item) + '\n'; }); - } else { + } + else { for (let p in obj) { if (obj.hasOwnProperty(p)) { str += obj[p] + '\n'; @@ -115,24 +130,27 @@ function fetchSoftwareProductCategories(dispatch) { }); return RestAPIUtil.fetch(softwareProductCategoriesUrl()) .then(handleResponse) - .fail(() => handleResponse(null)); + .catch(() => handleResponse(null)); } function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) { - LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}); - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion}); + return Promise.all([ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}), + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion}) + ]); } function getExpandedItemsId(items, itemIdToToggle) { - for(let i = 0; i < items.length; i++) { - if(items[i].id === itemIdToToggle) { + for (let i = 0; i < items.length; i++) { + if (items[i].id === itemIdToToggle) { if (items[i].expanded) { return {}; - } else { + } + else { return {[itemIdToToggle]: true}; } } - else if(items[i].items && items[i].items.length > 0) { + else if (items[i].items && items[i].items.length > 0) { let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle); if (mapOfExpandedIds !== false) { mapOfExpandedIds[items[i].id] = true; @@ -143,8 +161,63 @@ function getExpandedItemsId(items, itemIdToToggle) { return false; } +function getTimestampString() { + let date = new Date(); + let z = n => n < 10 ? '0' + n : n; + return `${date.getFullYear()}-${z(date.getMonth())}-${z(date.getDate())}_${z(date.getHours())}-${z(date.getMinutes())}`; +} + +function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) { + let filename; + let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : ''; + let match = contentDisposition.match(/filename=(.*?)(;|$)/); + if (match) { + filename = match[1]; + } + else { + filename = defaultFilename; + } + + if (addTimestamp) { + filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`); + } + + let link = document.createElement('a'); + let url = URL.createObjectURL(blob); + link.href = url; + link.download = filename; + link.style.display = 'none'; + document.body.appendChild(link); + link.click(); + setTimeout(function(){ + document.body.removeChild(link); + URL.revokeObjectURL(url); + }, 0); +} + +function migrateSoftwareProduct(vspId, version) { + return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`); +} + +function adjustMinorVersion(version, value) { + let ar = version.split('.'); + return ar[0] + '.' + (parseInt(ar[1]) + value); +} + +function adjustMajorVersion(version, value) { + let ar = version.split('.'); + return (parseInt(ar[0]) + value) + '.0'; +} + const SoftwareProductActionHelper = { + fetchFinalizedSoftwareProductList(dispatch) { + return fetchFinalizedSoftwareProductList().then(response => dispatch({ + type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED, + response + })); + }, + loadSoftwareProductAssociatedData(dispatch) { fetchSoftwareProductCategories(dispatch); LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); @@ -152,7 +225,7 @@ const SoftwareProductActionHelper = { loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) { SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); - loadLicensingData(dispatch, {licenseModelId, licensingVersion}); + return loadLicensingData(dispatch, {licenseModelId, licensingVersion}); }, fetchSoftwareProductList(dispatch) { @@ -162,37 +235,61 @@ const SoftwareProductActionHelper = { })); }, - uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle}) { + loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}){ + return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`).then(response => dispatch({ + type: HeatSetupActions.MANIFEST_LOADED, + response + })); + }, + + updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){ + return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version); + }, + + processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){ + return validateHeatCandidate(softwareProductId, version).then(response => { + if (response.status === 'Success') { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); + } + }); + }, + + uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) { + dispatch({ + type: HeatSetupActions.FILL_HEAT_SETUP_CACHE, + payload: {} + }); + Promise.resolve() - .then(() => uploadFile(softwareProductId, formData)) + .then(() => uploadFile(softwareProductId, formData, version)) .then(response => { - if (response.status !== 'Success') { + if (response.status === 'Success') { + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}); + } + else { throw new Error(parseUploadErrorMsg(response.errors)); } }) - .then(() => { - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId}); - OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); - SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); - }) .catch(error => { dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {title: failedNotificationTitle, msg: error.message} + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + title: failedNotificationTitle, + msg: error.message + } }); }); }, - uploadConfirmation(dispatch, {softwareProductId, formData, failedNotificationTitle}) { - dispatch({ - type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION, - uploadData: { - softwareProductId, - formData, - failedNotificationTitle - } - }); + downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){ + let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}); + p.then(() => { + fetchOrchestrationTemplateCandidate(softwareProductId, version) + .then((blob, statusText, xhr) => showFileSaveDialog({blob, xhr, defaultFilename: 'HEAT_file.zip', addTimestamp: true})); + }, null/* do not download if data was not saved correctly*/); }, + hideUploadConfirm (dispatch) { dispatch({ type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION @@ -208,7 +305,8 @@ const SoftwareProductActionHelper = { ), SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, { softwareProductId: softwareProduct.id, - qdata + qdata, + version: softwareProduct.version }) ]); }, @@ -217,8 +315,8 @@ const SoftwareProductActionHelper = { return putSoftwareProduct(softwareProduct); }, - updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata}) { - return putSoftwareProductQuestionnaire(softwareProductId, qdata); + updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) { + return putSoftwareProductQuestionnaire(softwareProductId, qdata, version); }, softwareProductEditorDataChanged(dispatch, {deltaData}) { @@ -235,10 +333,34 @@ const SoftwareProductActionHelper = { }); }, - softwareProductEditorVendorChanged(dispatch, {deltaData}) { - LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion}); - FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion}); - SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}); + softwareProductEditorVendorChanged(dispatch, {deltaData, formName}) { + if (deltaData.licensingVersion.id){ + let p = Promise.all([ + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, { + licenseModelId: deltaData.vendorId, + version: {id: deltaData.licensingVersion.id} + }), + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, { + licenseModelId: deltaData.vendorId, + version: {id: deltaData.licensingVersion.id} + }) + ]); + ValidationHelper.dataChanged(dispatch, {deltaData, formName}); + return p; + } else { + ValidationHelper.dataChanged(dispatch, {deltaData, formName}); + + dispatch({ + type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED, + response: {results: []} + }); + + dispatch({ + type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED, + response: {results: []} + }); + } + }, setIsValidityData(dispatch, {isValidityData}) { @@ -265,55 +387,67 @@ const SoftwareProductActionHelper = { return response; }), fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => { - dispatch({ - type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE, - payload: { - qdata: response.data ? JSON.parse(response.data) : {}, - qschema: JSON.parse(response.schema) - } - }); + ValidationHelper.qDataLoaded(dispatch, {response: {qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema)}, qName: PRODUCT_QUESTIONNAIRE}); }) ]); }, - performVCAction(dispatch, {softwareProductId, action}) { + performVCAction(dispatch, {softwareProductId, action, version}) { if (action === VersionControllerActionsEnum.SUBMIT) { - return putSoftwareProductAction(softwareProductId, action).then(() => { - return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE).then(() => { + return putSoftwareProductAction(softwareProductId, action, version).then(() => { + return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE, version).then(() => { dispatch({ - type: NotificationConstants.NOTIFY_SUCCESS, + type: modalActionTypes.GLOBAL_MODAL_SUCCESS, data: { title: i18n('Submit Succeeded'), msg: i18n('This software product successfully submitted'), + cancelButtonText: i18n('OK'), timeout: 2000 } }); - fetchSoftwareProduct(softwareProductId).then(response => { - dispatch({ - type: actionTypes.SOFTWARE_PRODUCT_LOADED, - response - }); - }); + const newVersionId = adjustMajorVersion(version.label, 1); + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}}); + return Promise.resolve({newVersion: {id: newVersionId}}); }); }, error => dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {title: i18n('Submit Failed'), validationResponse: error.responseJSON} + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE, + title: i18n('Submit Failed'), + modalComponentProps: { + validationResponse: error.responseJSON + }, + cancelButtonText: i18n('Ok') + } })); } else { - return putSoftwareProductAction(softwareProductId, action).then(() => { - fetchSoftwareProduct(softwareProductId).then(response => { - dispatch({ - type: actionTypes.SOFTWARE_PRODUCT_LOADED, - response - }); - }); + return putSoftwareProductAction(softwareProductId, action, version).then(() => { + let newVersionId = version.id; + /* + TODO Temorary switch to change version label + */ + switch(action) { + case VersionControllerActionsEnum.CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, 1); + break; + case VersionControllerActionsEnum.UNDO_CHECK_OUT: + newVersionId = adjustMinorVersion(version.label, -1); + break; + } + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}}); + return Promise.resolve({newVersion: {id: newVersionId}}); }); } }, switchVersion(dispatch, {softwareProductId, licenseModelId, version}) { - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version}); + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, { + softwareProductId, + licenseModelId, + version + }); }, toggleNavigationItems(dispatch, {items, itemIdToExpand}) { @@ -327,7 +461,17 @@ const SoftwareProductActionHelper = { /** for the next verision */ addComponent(dispatch) { return dispatch; + }, + + migrateSoftwareProduct(dispatch, {softwareProduct}) { + let {licenseModelId, licensingVersion, id: softwareProductId, version, status} = softwareProduct; + const newVer = status === statusEnum.CHECK_IN_STATUS || status === statusEnum.SUBMIT_STATUS ? + adjustMinorVersion(version.id, 1) : version.id; + migrateSoftwareProduct(softwareProductId, version) + .then(() =>OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, + {softwareProductId, version: {id: newVer, label: newVer}, licenseModelId, licensingVersion})); } + }; export default SoftwareProductActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js index 812afe5409..9b147415f9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - export default { getCurrentCategoryOfSubCategory(selectedSubCategory, softwareProductCategories) { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js index 5f10c27084..f29b0f6e0d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js @@ -1,28 +1,25 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; +import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; export const actionTypes = keyMirror({ SOFTWARE_PRODUCT_LOADED: null, SOFTWARE_PRODUCT_LIST_LOADED: null, + FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED: null, SOFTWARE_PRODUCT_LIST_EDIT: null, SOFTWARE_PRODUCT_CATEGORIES_LOADED: null, SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE: null, @@ -33,21 +30,46 @@ export const actionTypes = keyMirror({ OPEN: null, CLOSE: null, DATA_CHANGED: null, - IS_VALIDITY_DATA_CHANGED: null, - UPLOAD_CONFIRMATION: null + IS_VALIDITY_DATA_CHANGED: null } }); export const navigationItems = keyMirror({ - VENDOR_SOFTWARE_PRODUCT: 'Vendor Software Product', - GENERAL: 'General', - PROCESS_DETAILS: 'Process Details', - NETWORKS: 'Networks', - ATTACHMENTS: 'Attachments', - COMPONENTS: 'Components', + VENDOR_SOFTWARE_PRODUCT: 'vendor-software-product', + GENERAL: 'general', + PROCESS_DETAILS: 'process-details', + NETWORKS: 'networks', + DEPENDENCIES: 'dependencies', + ATTACHMENTS: 'attachments', + ACTIVITY_LOG: 'activity-log', + COMPONENTS: 'components', + + COMPUTE: 'compute', + LOAD_BALANCING: 'load-balancing', + STORAGE: 'storage', + MONITORING: 'monitoring' +}); - COMPUTE: 'Compute', - LOAD_BALANCING: 'Load Balancing', - STORAGE: 'Storage', - MONITORING: 'Monitoring' +export const forms = keyMirror({ + VENDOR_SOFTWARE_PRODUCT_DETAILS: 'vendor-software-product-details', }); + +export const PRODUCT_QUESTIONNAIRE = 'product'; + +export const mapScreenToNavigationItem = { + [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: navigationItems.VENDOR_SOFTWARE_PRODUCT, + [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: navigationItems.GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: navigationItems.ATTACHMENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG]: navigationItems.ACTIVITY_LOG, + [enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES]: navigationItems.DEPENDENCIES, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: navigationItems.COMPONENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: navigationItems.GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: navigationItems.COMPUTE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: navigationItems.LOAD_BALANCING, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING, +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js index 6d1db1626f..2fde8c2216 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js index 784ac9db84..97988d87f9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -1,26 +1,23 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {combineReducers} from 'redux'; -import {actionTypes} from './SoftwareProductConstants.js'; -import SoftwareProductAttachmentsReducer from './attachments/SoftwareProductAttachmentsReducer.js'; +import {actionTypes, PRODUCT_QUESTIONNAIRE} from './SoftwareProductConstants.js'; +import HeatValidationReducer from './attachments/validation/HeatValidationReducer.js'; +import HeatSetupReducer from './attachments/setup/HeatSetupReducer.js'; +import {actionTypes as heatSetupActionTypes} from './attachments/setup/HeatSetupConstants.js'; import SoftwareProductCreationReducer from './creation/SoftwareProductCreationReducer.js'; import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js'; import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js'; @@ -35,30 +32,40 @@ import {actionTypes as componentProcessesActionTypes} from './components/proces import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js'; import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; +import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; +import SoftwareProductDependenciesReducer from './dependencies/SoftwareProductDependenciesReducer.js'; +import {createJSONSchemaReducer, createComposedJSONSchemaReducer} from 'sdc-app/common/reducers/JSONSchemaReducer.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; export default combineReducers({ - softwareProductAttachments: SoftwareProductAttachmentsReducer, - softwareProductCreation: SoftwareProductCreationReducer, - softwareProductEditor: SoftwareProductDetailsReducer, + softwareProductAttachments: combineReducers({ + heatValidation: HeatValidationReducer, + heatSetup: HeatSetupReducer, + heatSetupCache: (state = {}, action) => action.type === heatSetupActionTypes.FILL_HEAT_SETUP_CACHE ? action.payload : state + }), + softwareProductCreation: createPlainDataReducer(SoftwareProductCreationReducer), + softwareProductEditor: createPlainDataReducer(SoftwareProductDetailsReducer), softwareProductProcesses: combineReducers({ processesList: SoftwareProductProcessesListReducer, - processesEditor: SoftwareProductProcessesEditorReducer, + processesEditor: createPlainDataReducer(SoftwareProductProcessesEditorReducer), processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state }), softwareProductNetworks: combineReducers({ networksList: SoftwareProductNetworksListReducer }), + softwareProductDependencies: SoftwareProductDependenciesReducer, softwareProductComponents: combineReducers({ componentsList: SoftwareProductComponentsListReducer, - componentEditor: SoftwareProductComponentEditorReducer, + componentEditor: createPlainDataReducer(createComposedJSONSchemaReducer(COMPONENTS_QUESTIONNAIRE, SoftwareProductComponentEditorReducer)), componentProcesses: combineReducers({ processesList: SoftwareProductComponentProcessesListReducer, - processesEditor: SoftwareProductComponentProcessesEditorReducer, + processesEditor: createPlainDataReducer(SoftwareProductComponentProcessesEditorReducer), processToDelete: (state = false, action) => action.type === componentProcessesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM ? action.processToDelete : state, }), network: combineReducers({ nicList: SoftwareProductComponentsNICListReducer, - nicEditor: SoftwareProductComponentsNICEditorReducer + nicEditor: createPlainDataReducer(createComposedJSONSchemaReducer(NIC_QUESTIONNAIRE, SoftwareProductComponentsNICEditorReducer)) }), monitoring: SoftwareProductComponentsMonitoringReducer }), @@ -68,13 +75,5 @@ export default combineReducers({ } return state; }, - softwareProductQuestionnaire: (state = {}, action) => { - if (action.type === actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE) { - return { - ...state, - ...action.payload - }; - } - return state; - } + softwareProductQuestionnaire: createJSONSchemaReducer(PRODUCT_QUESTIONNAIRE) }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js index a4b95a4b7e..8f2506abdd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js @@ -1,42 +1,89 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import HeatSetupActionHelper from './setup/HeatSetupActionHelper.js'; import SoftwareProductAttachmentsView from './SoftwareProductAttachmentsView.jsx'; -import SoftwareProductAttachmentsActionHelper from './SoftwareProductAttachmentsActionHelper.js'; +import {errorLevels} from 'sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import HeatSetup from './setup/HeatSetup.js'; +import {doesHeatDataExist} from './SoftwareProductAttachmentsUtils.js'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = (state) => { + let { + softwareProduct: { + softwareProductEditor:{data: currentSoftwareProduct = {}}, + softwareProductAttachments: {heatSetup, heatSetupCache, heatValidation : {errorList}} + } + } = state; + + let {unassigned = [], modules = []} = heatSetup; + let goToOverview = true; + if (errorList) { + for (let i = 0 ; i < errorList.length ; i++) { + if (errorList[i].level === errorLevels.ERROR) { + goToOverview = false; + } + } + } + let heatDataExist = doesHeatDataExist(heatSetup); -export const mapStateToProps = ({softwareProduct: {softwareProductAttachments}}) => { - let {attachmentsTree, hoveredNode, selectedNode, errorList} = softwareProductAttachments; + let isReadOnlyMode = currentSoftwareProduct && currentSoftwareProduct.version ? + VersionControllerUtils.isReadOnly(currentSoftwareProduct) : false; + let {version} = currentSoftwareProduct; return { - attachmentsTree, - hoveredNode, - selectedNode, - errorList + isValidationAvailable: unassigned.length === 0 && modules.length > 0, + heatSetup, + heatSetupCache, + heatDataExist, + goToOverview, + HeatSetupComponent: HeatSetup, + isReadOnlyMode, + version }; }; -const mapActionsToProps = (dispatch) => { +export const mapActionsToProps = (dispatch, {softwareProductId}) => { return { - toggleExpanded: (path) => SoftwareProductAttachmentsActionHelper.toggleExpanded(dispatch, {path}), - onSelectNode: (nodeName) => SoftwareProductAttachmentsActionHelper.onSelectNode(dispatch, {nodeName}), - onUnselectNode: () => SoftwareProductAttachmentsActionHelper.onUnselectNode(dispatch) + onDownload: ({heatCandidate, isReadOnlyMode, version}) => SoftwareProductActionHelper.downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}), + onUpload: (formData, version) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Upload will erase existing data. Do you want to continue?'), + confirmationButtonText: i18n('Continue'), + onConfirmed: ()=>SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed'), + version + }) + } + }), + onSave: (heatCandidate, version) => SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}), + onGoToOverview: () => { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId}); + }, + onProcessAndValidate: ({heatData, heatDataCache, isReadOnlyMode, version}) => { + return HeatSetupActionHelper.processAndValidateHeat(dispatch, + {softwareProductId, heatData, heatDataCache, isReadOnlyMode, version}); + } }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js deleted file mode 100644 index a7f7a5173b..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; - -export default { - - toggleExpanded(dispatch, {path}) { - dispatch({ - type: actionTypes.TOGGLE_EXPANDED, - path - }); - }, - - onSelectNode(dispatch, {nodeName}) { - dispatch({ - type: actionTypes.SELECTED_NODE, - nodeName - }); - }, - - onUnselectNode(dispatch) { - dispatch({ - type: actionTypes.UNSELECTED_NODE - }); - } -}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js index 33af476d9c..b0410d1566 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js @@ -1,55 +1,19 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import keyMirror from 'nfvo-utils/KeyMirror.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; - -export const actionTypes = keyMirror({ - TOGGLE_EXPANDED: null, - SELECTED_NODE: null, - UNSELECTED_NODE: null -}); - -export const errorTypes = keyMirror({ - MISSING_FILE_IN_ZIP: i18n('missing file in zip'), - MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'), - MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'), - FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'), - FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'), - ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'), - ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'), - MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'), - MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'), - ARTIFACT_NOT_IN_USE: i18n('artifact not in use') -}); - -export const nodeTypes = keyMirror({ - heat: i18n('Heat'), - volume: i18n('Volume'), - network: i18n('Network'), - artifact: i18n('Artifact'), - env: i18n('Environment'), - other: i18n('') -}); - -export const mouseActions = keyMirror({ - MOUSE_BUTTON_CLICK: 0 -}); - +export const tabsMapping = { + SETUP: 1, + VALIDATION: 2 +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js deleted file mode 100644 index 5c5567b032..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js +++ /dev/null @@ -1,199 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; - -const mapVolumeData = ({fileName, env, errors}) => ({ - name: fileName, - expanded: true, - type: 'volume', - children: env && [{ - name: env.fileName, - errors: env.errors, - type: 'env' - }], - errors -}); - -const mapNetworkData = ({fileName, env, errors}) => ({ - name: fileName, - expanded: true, - type: 'network', - children: env && [{ - name: env.fileName, - errors: env.errors, - type: 'env' - }], - errors -}); - -const mapArtifactsData = ({fileName, errors}) => ({ - name: fileName, - type: 'artifact', - errors -}); - -const mapOtherData = ({fileName, errors}) => ({ - name: fileName, - type: 'other', - errors -}); - - -const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({ - name: fileName, - expanded: true, - type: 'heat', - errors, - children: [ - ...(volume ? volume.map(mapVolumeData) : []), - ...(network ? network.map(mapNetworkData) : []), - ...(env ? [{ - name: env.fileName, - errors: env.errors, - type: 'env' - }] : []), - ...(artifacts ? artifacts.map(mapArtifactsData) : []), - ...(other ? other.map(mapOtherData) : []), - ...(nested ? nested.map(mapHeatData) : []) - ] -}); - -function createErrorList(node, parent, deep = 0, errorList = []) { - if (node.errors) { - errorList.push(...node.errors.map((error) => ({ - errorLevel: error.level, - errorMessage: error.message, - name: node.name, - hasParent: deep > 2, - parentName: parent.name, - type: node.type, - }))); - } - if (node.children && node.children.length) { - node.children.map((child) => createErrorList(child, node, deep + 1, errorList)); - } - return errorList; -} - -const mapValidationDataToTree = validationData => { - let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {}; - return { - children: [ - { - name: 'HEAT', - expanded: true, - type: 'heat', - children: (HEAT ? HEAT.map(mapHeatData) : []) - }, - ...(artifacts ? [{ - name: 'artifacts', - expanded: true, - type: 'artifact', - children: (artifacts ? artifacts.map(mapArtifactsData) : []) - }] : []), - ...(network ? [{ - name: 'networks', - expanded: true, - type: 'network', - children: (network ? network.map(mapNetworkData) : []), - }] : []), - ...(volume ? [{ - name: 'volume', - expanded: true, - type: 'volume', - children: (volume ? volume.map(mapVolumeData) : []), - }] : []), - ...(other ? [{ - name: 'other', - expanded: true, - type: 'other', - children: (other ? other.map(mapOtherData) : []), - }] : []) - ] - }; -}; - -const toggleExpanded = (node, path) => { - let newNode = {...node}; - if (path.length === 0) { - newNode.expanded = !node.expanded; - } else { - let index = path[0]; - newNode.children = [ - ...node.children.slice(0, index), - toggleExpanded(node.children[index], path.slice(1)), - ...node.children.slice(index + 1) - ]; - } - return newNode; -}; - -const expandSelected = (node, selectedNode) => { - let shouldExpand = node.name === selectedNode; - let children = node.children && node.children.map(child => { - let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode); - shouldExpand = shouldExpand || shouldExpandChild; - return newChild; - }); - - return { - node: { - ...node, - expanded: node.expanded || shouldExpand, - children - }, - shouldExpand - }; -}; - -export default (state = {attachmentsTree: {}}, action) => { - switch (action.type) { - case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED: - let currentSoftwareProduct = action.response; - let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {}; - let errorList = createErrorList(attachmentsTree); - return { - ...state, - attachmentsTree, - errorList - }; - case actionTypes.TOGGLE_EXPANDED: - return { - ...state, - attachmentsTree: toggleExpanded(state.attachmentsTree, action.path) - }; - case actionTypes.SELECTED_NODE: - let selectedNode = action.nodeName; - return { - ...state, - attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node, - selectedNode - }; - case actionTypes.UNSELECTED_NODE: - return { - ...state, - selectedNode: undefined - }; - default: - return state; - } -}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsUtils.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsUtils.js new file mode 100644 index 0000000000..2e76b11630 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsUtils.js @@ -0,0 +1,25 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +export function doesHeatDataExist(heatData) { + let result = false; + for (let key of Object.keys(heatData)) { + if(heatData[key].length > 0) { + result = true; + } + } + return result; +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx index c52999ca46..66fb2f8356 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx @@ -1,182 +1,119 @@ -import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import classNames from 'classnames'; -import Collapse from 'react-bootstrap/lib/Collapse.js'; - +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {Component, PropTypes} from 'react'; +import Tabs from 'react-bootstrap/lib/Tabs.js'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import {tabsMapping} from './SoftwareProductAttachmentsConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import {nodeTypes, mouseActions} from './SoftwareProductAttachmentsConstants'; - -const typeToIcon = Object.freeze({ - heat: 'building-o', - volume: 'database', - network: 'cloud', - artifact: 'gear', - env: 'server', - other: 'cube' -}); +import Icon from 'nfvo-components/icon/Icon.jsx'; +import HeatValidation from './validation/HeatValidation.js'; -const leftPanelWidth = 250; - -class SoftwareProductAttachmentsView extends React.Component { +class HeatScreenView extends Component { static propTypes = { - attachmentsTree: React.PropTypes.object.isRequired + isValidationAvailable: PropTypes.bool, + goToOverview: PropTypes.bool }; + state = { - treeWidth: '400' + activeTab: tabsMapping.SETUP }; render() { - let {attachmentsTree, errorList} = this.props; - let {treeWidth} = this.state; + let {isValidationAvailable, isReadOnlyMode, heatDataExist, onDownload, softwareProductId, onProcessAndValidate, heatSetup, HeatSetupComponent, onGoToOverview, version, ...other} = this.props; return ( - <div className='software-product-attachments'> - <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}> - <div className='tree-wrapper'> - { - attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind])) - } - </div> + <div className='vsp-attachments-view'> + <div className='attachments-view-controllers'> + {(this.state.activeTab === tabsMapping.SETUP) && + <Icon + iconClassName={heatDataExist ? '' : 'disabled'} + className={heatDataExist ? '' : 'disabled'} + image='download' + label={i18n('Download HEAT')} + onClick={heatDataExist ? () => onDownload({heatCandidate: heatSetup, isReadOnlyMode, version}) : undefined} + data-test-id='download-heat'/>} + {(this.state.activeTab === tabsMapping.VALIDATION && softwareProductId) && + <Icon + iconClassName={this.props.goToOverview ? '' : 'disabled'} + className={`go-to-overview-icon ${this.props.goToOverview ? '' : 'disabled'}`} + labelClassName='go-to-overview-label' + onClick={this.props.goToOverview ? onGoToOverview : undefined} + image='go-to-overview' + label={i18n('Go to Overview')} + data-test-id='go-to-overview'/>} + <Icon + image='upload' + label={i18n('Upload New HEAT')} + className={isReadOnlyMode ? 'disabled' : ''} + iconClassName={isReadOnlyMode ? 'disabled' : ''} + onClick={evt => {this.refs.hiddenImportFileInput.click(evt);}} + data-test-id='upload-heat'/> + <input + ref='hiddenImportFileInput' + type='file' + name='fileInput' + accept='.zip' + onChange={evt => this.handleImport(evt)}/> </div> - <div onMouseDown={(e) => this.onChangeTreeWidth(e)} className='software-product-attachments-separator'/> - - <div className='software-product-attachments-error-list'> - {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ? - i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>} - </div> - </div> - ); - } - - renderNode(node, path) { - let isFolder = node.children && node.children.length > 0; - let {onSelectNode} = this.props; - return ( - <div key={node.name} className='tree-block-inside'> - { - <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}> - { - isFolder && - <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}> - <FontAwesome name='caret-down'/> - </div> - } - { - - <span className='tree-node-icon'> - <FontAwesome name={typeToIcon[node.type]}/> - </span> - } - { - - <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}> - {node.name} - </span> - } - </div> - } - { - isFolder && - <Collapse in={node.expanded}> - <div className='tree-node-children'> - { - node.children.map((child, ind) => this.renderNode(child, [...path, ind])) - } - </div> - </Collapse> - } + <Tabs id='attachments-tabs' activeKey={this.state.activeTab} onSelect={key => this.handleTabPress(key)}> + <Tab eventKey={tabsMapping.SETUP} title='HEAT Setup'> + <HeatSetupComponent + heatDataExist={heatDataExist} + changeAttachmentsTab={tab => this.setState({activeTab: tab})} + onProcessAndValidate={onProcessAndValidate} + softwareProductId={softwareProductId} + isReadOnlyMode={isReadOnlyMode} + version={version}/> + </Tab> + <Tab eventKey={tabsMapping.VALIDATION} title='Heat Validation' disabled={!isValidationAvailable}> + <HeatValidation {...other}/> + </Tab> + </Tabs> </div> ); } - createErrorList(errorList, node, parent) { - if (node.errors) { - node.errors.forEach(error => errorList.push({ - error, - name: node.name, - parentName: parent.name, - type: node.type - })); + handleTabPress(key) { + let {heatSetup, heatSetupCache, onProcessAndValidate, isReadOnlyMode, version} = this.props; + switch (key) { + case tabsMapping.VALIDATION: + onProcessAndValidate({heatData: heatSetup, heatDataCache: heatSetupCache, isReadOnlyMode, version}).then( + () => this.setState({activeTab: tabsMapping.VALIDATION}) + ); + return; + case tabsMapping.SETUP: + this.setState({activeTab: tabsMapping.SETUP}); + return; } - if (node.children && node.children.length) { - node.children.map((child) => this.createErrorList(errorList, child, node)); - } - } - - renderErrorList(errors) { - let prevError = {}; - let {selectedNode} = this.props; - return errors.map(error => { - let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName; - prevError = error; - - return ( - <div - key={error.name + error.errorMessage + error.parentName} - - onClick={() => this.selectNode(error.name)} - className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}> - <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}> - { - error.hasParent ? - i18n('{type} {name} in {parentName}: ', { - type: nodeTypes[error.type], - name: error.name, - parentName: error.parentName - }) : - i18n('{type} {name}: ', { - type: nodeTypes[error.type], - name: error.name - }) - } - </span> - <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span> - </div> - ); - }); } - selectNode(currentSelectedNode) { - let {onUnselectNode, onSelectNode, selectedNode} = this.props; - if (currentSelectedNode !== selectedNode) { - onSelectNode(currentSelectedNode); - }else{ - onUnselectNode(); - } - - } - - getTreeRowClassName(name) { - let {hoveredNode, selectedNode} = this.props; - return classNames({ - 'tree-node-row': true, - 'tree-node-selected': name === hoveredNode, - 'tree-node-clicked': name === selectedNode - }); + handleImport(evt) { + evt.preventDefault(); + let {version} = this.props; + let formData = new FormData(); + formData.append('upload', this.refs.hiddenImportFileInput.files[0]); + this.refs.hiddenImportFileInput.value = ''; + this.props.onUpload(formData, version); + this.setState({activeTab: tabsMapping.SETUP}); } - getTreeTextClassName(node) { - let {selectedNode} = this.props; - return classNames({ - 'tree-element-text': true, - 'error-status': node.errors, - 'error-status-selected': node.name === selectedNode - }); + save() { + return this.props.onSave(this.props.heatSetup, this.props.version); } - onChangeTreeWidth(e) { - if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { - let onMouseMove = (e) => { - this.setState({treeWidth: e.clientX - leftPanelWidth}); - }; - let onMouseUp = () => { - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onMouseUp); - }; - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - } - } } -export default SoftwareProductAttachmentsView; +export default HeatScreenView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetup.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetup.js new file mode 100644 index 0000000000..4c3adc6a7d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetup.js @@ -0,0 +1,62 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import HeatSetupView from './HeatSetupView.jsx'; +import HeatSetupActionHelper from './HeatSetupActionHelper.js'; + + +const BASE = true; + +function baseExists(modules) { + for (let i in modules) { + if (modules[i].isBase) { + return true; + } + } + return false; +} + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments: {heatSetup, heatSetupCache}}}) => { + let {modules = [], unassigned = [], artifacts = [], nested = []} = heatSetup; + let isBaseExist = baseExists(modules); + + return { + heatSetupCache, + modules, + unassigned, + artifacts, + nested, + isBaseExist + }; +}; + +export const mapActionsToProps = (dispatch, {}) => { + return { + onModuleRename: (oldName, newName) => HeatSetupActionHelper.renameModule(dispatch, {oldName, newName}), + onModuleAdd: () => HeatSetupActionHelper.addModule(dispatch, !BASE), + onBaseAdd: () => HeatSetupActionHelper.addModule(dispatch, BASE), + onModuleDelete: moduleName => HeatSetupActionHelper.deleteModule(dispatch, moduleName), + onModuleFileTypeChange: ({module, value, type}) => HeatSetupActionHelper.changeModuleFileType(dispatch, { + module, + value, + type + }), + onArtifactListChange: artifacts => HeatSetupActionHelper.changeArtifactList(dispatch, artifacts), + onAddAllUnassigned: () => HeatSetupActionHelper.addAllUnassignedFilesToArtifacts(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(HeatSetupView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js new file mode 100644 index 0000000000..53143647a3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js @@ -0,0 +1,77 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './HeatSetupConstants.js'; +import isEqual from 'lodash/isEqual.js'; +import cloneDeep from 'lodash/cloneDeep.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; + +export default { + + addModule(dispatch, isBase){ + dispatch({type: actionTypes.ADD_MODULE, data: {isBase}}); + }, + + deleteModule(dispatch, moduleName){ + dispatch({type: actionTypes.REMOVE_MODULE, data: {moduleName}}); + }, + + renameModule(dispatch, {oldName, newName}){ + dispatch({type: actionTypes.RENAME_MODULE, data: {oldName, newName}}); + }, + + changeModuleFileType(dispatch, {module, value, type}){ + if (!value) { + value = {value: ''}; + } + dispatch({type: actionTypes.FILE_ASSIGN_CHANGED, data: {module, value, type}}); + }, + + changeArtifactList(dispatch, artifacts){ + dispatch({type: actionTypes.ARTIFACT_LIST_CHANGE, data: {artifacts: artifacts.map(artifact => artifact.value)}}); + }, + + processAndValidateHeat(dispatch, {softwareProductId, heatData, heatDataCache, isReadOnlyMode, version}){ + return (isEqual({...heatData, softwareProductId}, heatDataCache) || isReadOnlyMode) ? Promise.resolve() : + SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate: heatData, version}) + .then(() => SoftwareProductActionHelper.processAndValidateHeatCandidate(dispatch, {softwareProductId, version})) + .then(() => dispatch({type: actionTypes.FILL_HEAT_SETUP_CACHE, payload: {...cloneDeep(heatData), softwareProductId}})); + }, + + addAllUnassignedFilesToArtifacts(dispatch){ + dispatch({type: actionTypes.ADD_ALL_UNASSIGNED_TO_ARTIFACTS}); + }, + + heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) { + return new Promise((resolve, reject) => { + if (isEqual({...heatSetup, softwareProductId}, heatSetupCache)) { + resolve(); + } else { + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`You have uploaded a new HEAT. If you navigate away or Check-in without proceeding to validation, + Old HEAT zip file will be in use. new HEAT will be ignored. Do you want to continue?`), + confirmationButtonText: i18n('Continue'), + onConfirmed: () => resolve(), + onDeclined: () => reject() + } + }); + } + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js new file mode 100644 index 0000000000..2d6bd574a7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js @@ -0,0 +1,42 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + + ARTIFACT_LIST_CHANGE: null, + ADD_ALL_UNASSIGNED_TO_ARTIFACTS: null, + ADD_ALL_ARTIFACTS_TO_UNASSIGNED: null, + + ADD_MODULE: null, + REMOVE_MODULE: null, + RENAME_MODULE: null, + FILL_HEAT_SETUP_CACHE: null, + FILE_ASSIGN_CHANGED: null, + + MANIFEST_LOADED: null, + + GO_TO_VALIDATION: null, + IN_VALIDATION: null + +}); + +export const fileTypes = { + YAML: {label: 'yaml', regex: /(yaml|yml)/g}, + ENV: {label: 'env', regex: /env/g}, + VOL: {label: 'vol', regex: /(yaml|yml)/g}, + VOL_ENV: {label: 'volEnv', regex: /env/g} +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupReducer.js new file mode 100644 index 0000000000..f49339ce35 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupReducer.js @@ -0,0 +1,124 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {actionTypes} from './HeatSetupConstants.js'; +import differenceWith from 'lodash/differenceWith.js'; + + +const emptyModule = (isBase, currentLength) => ({ + name: `${isBase ? 'base_' : 'module_'}${currentLength + 1}`, + isBase: isBase +}); + +function syncUnassignedFilesWithArtifactsChanges(unassigned, artifacts, oldArtifacts) { + if (artifacts.length > oldArtifacts.length) { + return differenceWith(unassigned, artifacts, (unassignedFile, artifact) => unassignedFile === artifact); + } + else { + const removedArtifact = differenceWith(oldArtifacts, artifacts, (oldArtifact, artifact) => artifact === oldArtifact); + return [...unassigned, removedArtifact[0]]; + } +} + +function findModuleIndexByName(modules, name) { + return modules.findIndex(module => module.name === name); +} + +function addDeletedModuleFilesToUnassigned(unassigned, deletedModule){ + let files = []; + for(let i in deletedModule){ + if (deletedModule.hasOwnProperty(i)) { + if (typeof deletedModule[i] === 'string' && deletedModule[i] && i !== 'name') { + files.push(deletedModule[i]); + } + } + } + + return unassigned.concat(files); +} + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.MANIFEST_LOADED: + return { + ...state, + ...action.response, + modules: action.response.modules.map(module => ({...module, name: module.name || module.yaml.substring(0, module.yaml.lastIndexOf('.'))})) + }; + case actionTypes.ARTIFACT_LIST_CHANGE: + return { + ...state, + artifacts: action.data.artifacts, + unassigned: syncUnassignedFilesWithArtifactsChanges(state.unassigned, action.data.artifacts, state.artifacts) + }; + case actionTypes.ADD_ALL_UNASSIGNED_TO_ARTIFACTS: + return { + ...state, + artifacts: [...state.artifacts,...state.unassigned], + unassigned: [] + }; + case actionTypes.ADD_ALL_ARTIFACTS_TO_UNASSIGNED: + return { + ...state, + artifacts: [], + unassigned: [...state.unassigned, ...state.artifacts] + }; + case actionTypes.ADD_MODULE: + return { + ...state, + modules: state.modules.concat({...emptyModule(action.data.isBase, state.modules.length)}) + }; + case actionTypes.REMOVE_MODULE: + const moduleIndexToDelete = findModuleIndexByName(state.modules, action.data.moduleName); + let unassigned = addDeletedModuleFilesToUnassigned(state.unassigned, state.modules[moduleIndexToDelete]); + return { + ...state, + unassigned, + modules: [...state.modules.slice(0, moduleIndexToDelete), ...state.modules.slice(moduleIndexToDelete + 1)] + }; + case actionTypes.RENAME_MODULE: + const indexToRename = findModuleIndexByName(state.modules, action.data.oldName); + let moduleToRename = state.modules[indexToRename]; + moduleToRename.name = action.data.newName; + return { + ...state, + modules: [...state.modules.slice(0, indexToRename), moduleToRename, ...state.modules.slice(indexToRename + 1)] + }; + case actionTypes.FILE_ASSIGN_CHANGED: + let {module, value:{value}, type} = action.data; + const moduleIndexToModify = findModuleIndexByName(state.modules, module.name); + let moduleToModify = state.modules[moduleIndexToModify]; + let dumpedFile = moduleToModify[type]; + if (dumpedFile !== value) { + if(value) { + moduleToModify[type] = value; + } + else { + delete moduleToModify[type]; + } + const newUnassignedList = dumpedFile ? [...state.unassigned.filter(file => file !== value), dumpedFile] : state.unassigned.filter(file => file !== value); + return { + ...state, + modules: [...state.modules.slice(0, moduleIndexToModify), moduleToModify, ...state.modules.slice(moduleIndexToModify + 1)], + unassigned: newUnassignedList + }; + } + else { + return state; + } + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx new file mode 100644 index 0000000000..0d8bc58361 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx @@ -0,0 +1,328 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {Component} from 'react'; +import Button from 'react-bootstrap/lib/Button.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import FormControl from 'react-bootstrap/lib/FormControl.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SelectInput from 'nfvo-components/input/SelectInput.jsx'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import {fileTypes} from './HeatSetupConstants.js'; +import {tabsMapping} from '../SoftwareProductAttachmentsConstants.js'; +import {sortable} from 'react-sortable'; + +class ListItem extends Component { + + render() { + return ( + <li {...this.props}>{this.props.children}</li> + ); + } +} + + +const SortableListItem = sortable(ListItem); + +class SortableModuleFileList extends Component { + + state = { + draggingIndex: null, + data: this.props.modules + }; + + + componentWillReceiveProps(nextProps) { + this.setState({data: nextProps.modules}); + } + + render() { + + let {unassigned, onModuleRename, onModuleDelete, onModuleAdd, onBaseAdd, onModuleFileTypeChange, isBaseExist} = this.props; + const childProps = module => ({ + module, + onModuleRename, + onModuleDelete, + onModuleFileTypeChange: (value, type) => onModuleFileTypeChange({module, value, type}), + files: unassigned + }); + let listItems = this.state.data.map(function (item, i) { + return ( + <SortableListItem + key={i} + updateState={data => this.setState(data)} + items={this.state.data} + draggingIndex={this.state.draggingIndex} + sortId={i} + outline='list'><ModuleFile {...childProps(item)} /></SortableListItem> + ); + }, this); + + return ( + <div className='modules-list-wrapper'> + <div className='modules-list-header'> + <div className='modules-list-controllers'> + {!isBaseExist && <Button bsStyle='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} + <Button bsStyle='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> + </div> + </div> + <ul>{listItems}</ul> + </div> + ); + } +} + +const tooltip = (name) => <Tooltip id='tooltip-bottom'>{name}</Tooltip>; +const UnassignedFileList = (props) => { + return ( + <div className='unassigned-files'> + <div className='unassigned-files-title'>{i18n('UNASSIGNED FILES')}</div> + <div className='unassigned-files-list'>{props.children}</div> + </div> + ); +}; + +const EmptyListContent = props => { + let {onClick, heatDataExist} = props; + let displayText = heatDataExist ? 'All Files Are Assigned' : ''; + return ( + <div className='go-to-validation-button-wrapper'> + <div className='all-files-assigned'>{i18n(displayText)}</div> + {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angle-right'/></div>} + </div> + ); +}; +const UnassignedFile = (props) => ( + <OverlayTrigger placement='bottom' overlay={tooltip(props.name)} delayShow={1000}> + <li data-test-id='unassigned-files' className='unassigned-files-list-item'>{props.name}</li> + </OverlayTrigger> +); + +const AddOrDeleteVolumeFiels = ({add = true, onAdd, onDelete}) => { + const displayText = add ? 'Add Volume Files' : 'Delete Volume Files'; + const action = add ? onAdd : onDelete; + return ( + <div className='add-or-delete-volumes' onClick={action}> + <SVGIcon name={add ? 'plus' : 'close'} /> + <span>{i18n(displayText)}</span> + </div> + ); +}; + +const SelectWithFileType = ({type, selected, files, onChange}) => { + + let filteredFiledAccordingToType = files.filter(file => file.label.search(type.regex) > -1); + if (selected) { + filteredFiledAccordingToType = filteredFiledAccordingToType.concat({label: selected, value: selected}); + } + + return ( + <SelectInput + data-test-id={`${type.label}-list`} + label={type.label} + value={selected} + onChange={value => value !== selected && onChange(value, type.label)} + disabled={filteredFiledAccordingToType.length === 0} + placeholder={filteredFiledAccordingToType.length === 0 ? '' : undefined} + clearable={true} + options={filteredFiledAccordingToType} /> + ); +}; + +class NameEditInput extends Component { + componentDidMount() { + this.input.focus(); + } + + render() { + return ( + <FormControl {...this.props} className='name-edit' inputRef={input => this.input = input}/> + ); + } +} + +class ModuleFile extends Component { + constructor(props) { + super(props); + this.state = { + isInNameEdit: false, + displayVolumes: Boolean(props.module.vol || props.module.volEnv) + }; + } + + handleSubmit(event, name) { + if (event.keyCode === 13) { + this.handleModuleRename(event, name); + } + } + + componentWillReceiveProps(nextProps) { + this.setState({displayVolumes: Boolean(nextProps.module.vol || nextProps.module.volEnv)}); + } + + handleModuleRename(event, name) { + this.setState({isInNameEdit: false}); + this.props.onModuleRename(name, event.target.value); + } + + deleteVolumeFiles() { + const { onModuleFileTypeChange} = this.props; + onModuleFileTypeChange(null, fileTypes.VOL.label); + onModuleFileTypeChange(null, fileTypes.VOL_ENV.label); + this.setState({displayVolumes: false}); + } + + renderNameAccordingToEditState() { + const {module: {name}} = this.props; + if (this.state.isInNameEdit) { + return (<NameEditInput defaultValue={name} onBlur={evt => this.handleModuleRename(evt, name)} onKeyDown={evt => this.handleSubmit(evt, name)}/>); + } + return (<span className='filename-text'>{name}</span>); + } + + render() { + const {module: {name, isBase, yaml, env, vol, volEnv}, onModuleDelete, files, onModuleFileTypeChange} = this.props; + const {displayVolumes} = this.state; + const moduleType = isBase ? 'BASE' : 'MODULE'; + return ( + <div className='modules-list-item' data-test-id='module-item'> + <div className='modules-list-item-controllers'> + <div className='modules-list-item-filename'> + <Icon image={isBase ? 'base' : 'module'} iconClassName='heat-setup-module-icon' /> + <span className='module-title-by-type'>{`${moduleType}: `}</span> + <div className={`text-and-icon ${this.state.isInNameEdit ? 'in-edit' : ''}`}> + {this.renderNameAccordingToEditState()} + {!this.state.isInNameEdit && <SVGIcon + name='pencil' + onClick={() => this.setState({isInNameEdit: true})} + data-test-id={isBase ? 'base-name' : 'module-name'}/>} + </div> + </div> + <SVGIcon name='trash-o' onClick={() => onModuleDelete(name)} data-test-id='module-delete'/> + </div> + <div className='modules-list-item-selectors'> + <SelectWithFileType + type={fileTypes.YAML} + files={files} + selected={yaml} + onChange={onModuleFileTypeChange}/> + <SelectWithFileType + type={fileTypes.ENV} + files={files} + selected={env} + onChange={onModuleFileTypeChange}/> + {displayVolumes && <SelectWithFileType + type={fileTypes.VOL} + files={files} + selected={vol} + onChange={onModuleFileTypeChange}/>} + {displayVolumes && <SelectWithFileType + type={fileTypes.VOL_ENV} + files={files} + selected={volEnv} + onChange={onModuleFileTypeChange}/>} + <AddOrDeleteVolumeFiels onAdd={() => this.setState({displayVolumes: true})} onDelete={() => this.deleteVolumeFiles()} add={!displayVolumes}/> + </div> + </div> + ); + } +} + +class ArtifactOrNestedFileList extends Component { + + render() { + let {type, title, selected, options, onSelectChanged, onAddAllUnassigned} = this.props; + return ( + <div className={`artifact-files ${type === 'nested' ? 'nested' : ''}`}> + <div className='artifact-files-header'> + <span> + {type === 'artifact' && (<Icon image='artifacts' iconClassName='heat-setup-module-icon' />)} + {`${title}`} + </span> + {type === 'artifact' && <span className='add-all-unassigned' onClick={onAddAllUnassigned}>{i18n('Add All Unassigned Files')}</span>} + </div> + {type === 'nested' ? ( + <ul className='nested-list'>{selected.map(nested => + <li key={nested} className='nested-list-item'>{nested}</li> + )}</ul>) : + (<SelectInput + options={options} + onMultiSelectChanged={onSelectChanged || (() => { + })} + value={selected} + clearable={false} + placeholder={i18n('Add Artifact')} + multi/>) + } + </div> + ); + } +} + +const buildLabelValueObject = str => (typeof str === 'string' ? {value: str, label: str} : str); + +class SoftwareProductHeatSetupView extends Component { + + processAndValidateHeat(heatData, heatDataCache){ + let {onProcessAndValidate, changeAttachmentsTab, version} = this.props; + onProcessAndValidate({heatData, heatDataCache, version}).then( + () => changeAttachmentsTab(tabsMapping.VALIDATION) + ); + } + + render() { + let {modules, heatSetupCache, isReadOnlyMode, heatDataExist, unassigned, artifacts, nested, onArtifactListChange, onAddAllUnassigned} = this.props; + + const formattedUnassigned = unassigned.map(buildLabelValueObject); + const formattedArtifacts = artifacts.map(buildLabelValueObject); + return ( + <div className={`heat-setup-view ${isReadOnlyMode ? 'disabled' : ''}`}> + <div className='heat-setup-view-modules-and-artifacts'> + <SortableModuleFileList + {...this.props} + artifacts={formattedArtifacts} + unassigned={formattedUnassigned}/> + <ArtifactOrNestedFileList + type={'artifact'} + title={i18n('ARTIFACTS')} + options={formattedUnassigned} + selected={formattedArtifacts} + onSelectChanged={onArtifactListChange} + onAddAllUnassigned={onAddAllUnassigned}/> + <ArtifactOrNestedFileList + type={'nested'} + title={i18n('NESTED HEAT FILES')} + options={[]} + selected={nested}/> + </div> + <UnassignedFileList> + { + formattedUnassigned.length > 0 ? + (<ul>{formattedUnassigned.map(file => <UnassignedFile key={file.label} name={file.label}/>)}</ul>) + : + (<EmptyListContent + heatDataExist={heatDataExist} + onClick={() => this.processAndValidateHeat({modules, unassigned, artifacts, nested}, heatSetupCache)}/>) + } + </UnassignedFileList> + </div> + ); + } + +} + +export default SoftwareProductHeatSetupView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js new file mode 100644 index 0000000000..21f6e6c77f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js @@ -0,0 +1,51 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import HeatValidationView from './HeatValidationView.jsx'; +import HeatValidationActionHelper from './HeatValidationActionHelper.js'; +import {errorLevels, nodeFilters} from './HeatValidationConstants.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments: {heatValidation}}}) => { + let {attachmentsTree, selectedNode, errorList} = heatValidation; + let currentErrors = [], currentWarnings = []; + if (errorList) { + for (let i = 0 ; i < errorList.length ; i++) { + if (errorList[i].level === errorLevels.ERROR && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) { + currentErrors[currentErrors.length] = errorList[i]; + } + if (errorList[i].level === errorLevels.WARNING && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) { + currentWarnings[currentWarnings.length] = errorList[i]; + } + } + } + return { + attachmentsTree, + selectedNode, + errorList, + currentErrors, + currentWarnings + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + toggleExpanded: (path) => HeatValidationActionHelper.toggleExpanded(dispatch, {path}), + onSelectNode: (nodeName) => HeatValidationActionHelper.onSelectNode(dispatch, {nodeName}), + onDeselectNode: () => HeatValidationActionHelper.onDeselectNode(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(HeatValidationView); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js index 15b0ffa4a9..73366c20cc 100644 --- a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js @@ -1,24 +1,19 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './AttachmentsConstants.js'; +import {actionTypes} from './HeatValidationConstants.js'; export default { @@ -36,7 +31,7 @@ export default { }); }, - onUnselectNode(dispatch) { + onDeselectNode(dispatch) { dispatch({ type: actionTypes.UNSELECTED_NODE }); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js index 33af476d9c..f783fe6482 100644 --- a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -40,6 +35,13 @@ export const errorTypes = keyMirror({ ARTIFACT_NOT_IN_USE: i18n('artifact not in use') }); +export const errorLevels = keyMirror({ + WARNING: 'WARNING', + ERROR: 'ERROR' +}); +export const nodeFilters = keyMirror({ + ALL: 'All' +}); export const nodeTypes = keyMirror({ heat: i18n('Heat'), volume: i18n('Volume'), diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js index 01f68aede8..f0c10ed457 100644 --- a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js @@ -1,25 +1,20 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; -import {actionTypes} from './AttachmentsConstants.js'; +import {actionTypes, nodeFilters} from './HeatValidationConstants.js'; const mapVolumeData = ({fileName, env, errors}) => ({ name: fileName, @@ -80,7 +75,7 @@ const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, function createErrorList(node, parent, deep = 0, errorList = []) { if (node.errors) { errorList.push(...node.errors.map((error) => ({ - errorLevel: error.level, + level: error.level, errorMessage: error.message, name: node.name, hasParent: deep > 2, @@ -95,14 +90,15 @@ function createErrorList(node, parent, deep = 0, errorList = []) { } const mapValidationDataToTree = validationData => { - let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {}; + let {heat, volume, network, artifacts, other} = validationData.importStructure || {}; return { children: [ { name: 'HEAT', expanded: true, type: 'heat', - children: (HEAT ? HEAT.map(mapHeatData) : []) + header: true, + children: (heat ? heat.map(mapHeatData) : []) }, ...(artifacts ? [{ name: 'artifacts', @@ -174,7 +170,8 @@ export default (state = {attachmentsTree: {}}, action) => { return { ...state, attachmentsTree, - errorList + errorList, + selectedNode: nodeFilters.ALL }; case actionTypes.TOGGLE_EXPANDED: return { @@ -191,7 +188,7 @@ export default (state = {attachmentsTree: {}}, action) => { case actionTypes.UNSELECTED_NODE: return { ...state, - selectedNode: undefined + selectedNode: nodeFilters.ALL }; default: return state; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx new file mode 100644 index 0000000000..25ad90f351 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx @@ -0,0 +1,274 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {Component, PropTypes} from 'react'; +import classNames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js'; + +const leftPanelWidth = 250; +const typeToIcon = Object.freeze({ + heat: 'heat', + volume: 'volume', + network: 'network', + artifact: 'validation-artifacts', + env: 'env', + other: 'validation-other' +}); + + +class HeatValidationView extends Component { + + static propTypes = { + attachmentsTree: PropTypes.object.isRequired, + errorList: PropTypes.array.isRequired, + currentErrors: PropTypes.array.isRequired, + currentWarnings: PropTypes.array.isRequired, + onSelectNode: PropTypes.func.isRequired, + onDeselectNode: PropTypes.func.isRequired, + toggleExpanded: PropTypes.func.isRequired, + selectedNode: PropTypes.string + }; + + render() { + return (<div className='vsp-attachments-heat-validation' data-test-id='heat-validation-editor'> + <HeatFileTree errorList={this.props.errorList} attachmentsTree={this.props.attachmentsTree} + onSelectNode={this.props.onSelectNode} toggleExpanded={this.props.toggleExpanded} + selectedNode={this.props.selectedNode} onDeselectNode={this.props.onDeselectNode} /> + <HeatMessageBoard errors={this.props.currentErrors} warnings={this.props.currentWarnings} selectedNode={this.props.selectedNode} /> + </div> ); + } +} + +function HeatFileTreeRow(props) { + let {node, path, toggleExpanded, selectedNode, selectNode} = props; + let isFolder = node.children && node.children.length > 0; + return ( + <div onDoubleClick={() => toggleExpanded(path)} className={classNames({ + 'tree-node-row': true, + 'tree-node-clicked': node.name === props.selectedNode + })} data-test-id='validation-tree-node'> + <div className='name-section'> + { + isFolder && + <div onClick={() => toggleExpanded(path)} + className='tree-node-expander'> + <SVGIcon name={!node.expanded ? 'chevron-up' : 'chevron-down'} data-test-id='validation-tree-block-toggle'/> + </div> + } + { + + <span className='tree-node-icon'> + <Icon image={typeToIcon[node.type]} iconClassName={selectedNode === node.name ? 'selected' : ''}/> + </span> + } + { + + <span className='tree-node-name' onClick={() => selectNode(node.name)} data-test-id='validation-tree-node-name'> + {node.name ? node.name : 'UNKNOWN'} + </span> + } + </div> + <ErrorsAndWarningsCount errorList={node.errors} onClick={() => selectNode(node.name)} /> + </div>); +} + +function HeatFileTreeHeader(props) { + let hasErrors = props.errorList.filter(error => error.level === errorLevels.ERROR).length > 0; + return ( + <div onClick={() => props.selectNode(nodeFilters.ALL)} className={classNames({'attachments-tree-header': true, + 'header-selected' : props.selectedNode === nodeFilters.ALL})} data-test-id='validation-tree-header'> + <div className='tree-header-title' > + <Icon image='zip' iconClassName={classNames(props.selectedNode === nodeFilters.ALL ? 'selected' : '', 'header-icon')} /> + <span className={classNames({'tree-header-title-text' : true, + 'tree-header-title-selected' : props.selectedNode === nodeFilters.ALL})}>{i18n(`HEAT${hasErrors ? ' (Draft)' : ''}`)}</span> + </div> + <ErrorsAndWarningsCount errorList={props.errorList} size='large' /> + </div>); +} + +class HeatFileTree extends React.Component { + static propTypes = { + attachmentsTree: PropTypes.object.isRequired, + errorList: PropTypes.array.isRequired, + onSelectNode: PropTypes.func.isRequired, + onDeselectNode: PropTypes.func.isRequired, + toggleExpanded: PropTypes.func.isRequired, + selectedNode: PropTypes.string + }; + state = { + treeWidth: '400' + }; + render() { + let {attachmentsTree} = this.props; + return ( + <div className='validation-tree-section' style={{'width' : this.state.treeWidth + 'px'}}> + <div className='vsp-attachments-heat-validation-tree'> + <div className='tree-wrapper'> + {attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind]))} + </div> + </div> + <div onMouseDown={(e) => this.onChangeTreeWidth(e)} + className='vsp-attachments-heat-validation-separator' data-test-id='validation-tree-separator'></div> + </div>); + } + renderNode(node, path) { + let rand = Math.random() * (3000 - 1) + 1; + let isFolder = node.children && node.children.length > 0; + let {selectedNode} = this.props; + return ( + <div key={node.name + rand} className={classNames({'tree-block-inside' : !node.header})}> + { + node.header ? + <HeatFileTreeHeader selectedNode={selectedNode} errorList={this.props.errorList} selectNode={(nodeName) => this.selectNode(nodeName)} /> : + <HeatFileTreeRow toggleExpanded={this.props.toggleExpanded} node={node} path={path} selectedNode={selectedNode} selectNode={() => this.selectNode(node.name)} /> + } + { + isFolder && + <Collapse in={node.expanded}> + <div className='tree-node-children'> + { + node.children.map((child, ind) => this.renderNode(child, [...path, ind])) + } + </div> + </Collapse> + } + </div> + ); + } + + + + + + selectNode(currentSelectedNode) { + let {onDeselectNode, onSelectNode, selectedNode} = this.props; + if (currentSelectedNode !== selectedNode) { + onSelectNode(currentSelectedNode); + } else { + onDeselectNode(); + } + + + + } + + onChangeTreeWidth(e) { + if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { + let onMouseMove = (e) => { + this.setState({treeWidth: e.clientX - leftPanelWidth}); + }; + let onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } + } +} + +class HeatMessageBoard extends Component { + static propTypes = { + currentErrors: PropTypes.array, + currentWarnings: PropTypes.array, + selectedNode: PropTypes.string + }; + render() { + let {errors, warnings} = this.props; + let allItems = [...errors, ...warnings]; + return ( + <div className='message-board-section'> + { allItems.map(error => this.renderError(error)) } + </div> + ); + } + renderError(error) { + let rand = Math.random() * (3000 - 1) + 1; + return ( + <div + key={error.name + error.errorMessage + error.parentName + rand} + className='error-item' data-test-id='validation-error'> + {error.level === errorLevels.WARNING ? + <SVGIcon name='exclamation-triangle-line' iconClassName='large' /> : <Icon image='error-lg' /> } + <span className='error-item-file-type'> + { + (this.props.selectedNode === nodeFilters.ALL) ? + <span> + <span className='error-file-name'> + {i18n('{errorName}:', { + errorName: error.name + })} + </span> + <span> + {i18n('{message}', {message: error.errorMessage})} + </span> + </span> : + i18n('{errorMsg}', { + errorMsg: error.errorMessage + }) + } + </span> + </div> + ); + } +} +class ErrorsAndWarningsCount extends Component { + static propTypes = { + errorList: PropTypes.array, + size: PropTypes.string + }; + render() { + let errors = this.getErrorsAndWarningsCount(this.props.errorList); + if (!errors) { + return null; + } + let errIcon = 'error'; + let {size} = this.props; + if (size && size === 'large') { + errIcon += '-lg'; + } + return (<div className='counters'> + {(errors.errorCount > 0) && <div className='counter'> + <Icon image={errIcon} iconClassName='counter-icon'/> + <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>{errors.errorCount}</div> + </div>} + {(errors.warningCount > 0) && <div className='counter'> + <SVGIcon name='exclamation-triangle-line' iconClassName={size} /> + <div className={'warning-text ' + (size ? size : '')} data-test-id='validation-warning-count'>{errors.warningCount}</div> + </div>} + </div>); + } + getErrorsAndWarningsCount(errorList) { + let errorCount = 0, warningCount = 0; + if (errorList && errorList.length > 0) { + for (let i = 0; i < errorList.length; i++) { + if (errorList[i].level === errorLevels.ERROR) { + errorCount++; + } else if (errorList[i].level === errorLevels.WARNING) { + warningCount++; + } + } + } + if (errorCount === 0 && warningCount === 0) { + return null; + } + return {errorCount, warningCount}; + } +} +export default HeatValidationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js index 3b8bc4f171..2ae9ad0bae 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js @@ -1,44 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './SoftwareProductComponentsConstants.js'; +import {actionTypes, forms} from './SoftwareProductComponentsConstants.js'; export default (state = {}, action) => { switch (action.type) { - case actionTypes.COMPONENT_UPDATE: - return { - ...state, - data: action.component - }; - case actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE: - return { - ...state, - qdata: action.payload.qdata || state.qdata, - qschema: action.payload.qschema || state.qschema - }; - case actionTypes.COMPONENT_DATA_CHANGED: + case actionTypes.COMPONENT_LOAD: return { ...state, - data: { - ...state.data, - ...action.deltaData + formReady: null, + formName: forms.ALL_SPC_FORMS, + genericFieldInfo: { + 'displayName' : { + isValid: true, + errorText: '', + validations: [] + }, + 'vfcCode' : { + isValid: true, + errorText: '', + validations: [] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [] + } } }; default: diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js index e53b2ecafe..9b3c9eaa73 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -1,56 +1,51 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -import {actionTypes} from './SoftwareProductComponentsConstants.js'; +import {actionTypes, COMPONENTS_QUESTIONNAIRE, forms} from './SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; -function baseUrl(softwareProductId) { +function baseUrl(softwareProductId, version) { + const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components`; + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components`; } function fetchSoftwareProductComponents(softwareProductId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); } -function putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, vspComponent) { - return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire`, vspComponent); +function putSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId, vspComponent) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${vspComponentId}/questionnaire`, vspComponent); } -function fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version){ - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire${versionQuery}`); +function fetchSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId){ + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}/${vspComponentId}/questionnaire`); } -function fetchSoftwareProductComponent(softwareProductId, vspComponentId, version){ - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}${versionQuery}`); +function fetchSoftwareProductComponent(softwareProductId, version, vspComponentId){ + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}/${vspComponentId}`); } -function putSoftwareProductComponent(softwareProductId, vspComponentId, vspComponent) { - return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}`, { +function putSoftwareProductComponent(softwareProductId, version, vspComponentId, vspComponent) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${vspComponentId}`, { name: vspComponent.name, displayName: vspComponent.displayName, + vfcCode: vspComponent.vfcCode, description: vspComponent.description }); } @@ -65,27 +60,19 @@ const SoftwareProductComponentsActionHelper = { }); }, - componentDataChanged(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.COMPONENT_DATA_CHANGED, - deltaData - }); - }, - - - updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, componentData, qdata}) { + updateSoftwareProductComponent(dispatch, {softwareProductId, version, vspComponentId, componentData, qdata}) { return Promise.all([ - SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}), - SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId, qdata}), + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, version, vspComponentId, componentData}) ]); }, - updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}) { - return putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, qdata); + updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId, qdata}) { + return putSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId, qdata); }, - updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) { - return putSoftwareProductComponent(softwareProductId, vspComponentId, componentData).then(() => dispatch({ + updateSoftwareProductComponentData(dispatch, {softwareProductId, version, vspComponentId, componentData}) { + return putSoftwareProductComponent(softwareProductId, version, vspComponentId, componentData).then(() => dispatch({ type: actionTypes.COMPONENTS_LIST_EDIT, component: { id: vspComponentId, @@ -94,36 +81,36 @@ const SoftwareProductComponentsActionHelper = { })); }, - - fetchSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, version}) { - return fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version).then(response => { - dispatch({ - type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, - payload: { - qdata: response.data ? JSON.parse(response.data) : {}, - qschema: JSON.parse(response.schema) - } - }); + fetchSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId}) { + return fetchSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId).then(response => { + ValidationHelper.qDataLoaded(dispatch, {qName: COMPONENTS_QUESTIONNAIRE, response: {qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema)}}); }); }, - fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, version}) { - return fetchSoftwareProductComponent(softwareProductId, vspComponentId, version).then(response => { - dispatch({ - type: actionTypes.COMPONENT_UPDATE, - component: response.data - }); + fetchSoftwareProductComponent(dispatch, {softwareProductId, version, vspComponentId}) { + dispatch({ + type: actionTypes.COMPONENT_LOAD }); + return Promise.all([ + fetchSoftwareProductComponent(softwareProductId, version, vspComponentId).then(response => { + ValidationHelper.dataChanged(dispatch,{deltaData: response.data, formName: forms.ALL_SPC_FORMS}); + return response; + }), + fetchSoftwareProductComponentQuestionnaire(softwareProductId, version, vspComponentId).then(response => { + ValidationHelper.qDataLoaded(dispatch, {qName: COMPONENTS_QUESTIONNAIRE, response: {qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema)}}); + }) + ]); }, - componentQuestionnaireUpdated(dispatch, {data}) { + + clearComponentsStore(dispatch) { dispatch({ - type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, - payload: { - qdata: data - } + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: [] }); - }, + } }; export default SoftwareProductComponentsActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js index dee517e5d1..9307b099ed 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js @@ -1,31 +1,24 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ COMPONENTS_LIST_UPDATE: null, COMPONENTS_LIST_EDIT: null, - COMPONENT_UPDATE: null, - COMPONENT_DATA_CHANGED: null, - COMPONENT_QUESTIONNAIRE_UPDATE: null + COMPONENT_LOAD: null }); export const storageConstants = keyMirror({ @@ -35,12 +28,18 @@ export const storageConstants = keyMirror({ } }); +export const forms = keyMirror({ + ALL_SPC_FORMS: null, + NIC_EDIT_FORM: null +}); + +export const COMPONENTS_QUESTIONNAIRE = 'component'; + export const navigationItems = keyMirror({ STORAGE: 'Storage', PROCESS_DETAILS: 'Process Details', MONITORING: 'Monitoring', NETWORK: 'Network', COMPUTE: 'Compute', - NETWORK: 'Network', LOAD_BALANCING: 'High Availability & Load Balancing' }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js index f1c1ed1fcc..f789a92c6f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import SoftwareProductComponentsListView from './SoftwareProductComponentsListView.jsx'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; @@ -37,9 +32,9 @@ const mapStateToProps = ({softwareProduct}) => { }; -const mapActionToProps = (dispatch, {version}) => { +const mapActionToProps = (dispatch) => { return { - onComponentSelect: ({id: softwareProductId, componentId}) => { + onComponentSelect: ({id: softwareProductId, componentId, version}) => { OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js index b91362a0cf..c7aaca5573 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductComponentsConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx index 0c0ba0f646..c28831fbde 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx @@ -1,8 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; const ComponentPropType = React.PropTypes.shape({ id: React.PropTypes.string, @@ -43,8 +59,9 @@ class SoftwareProductComponentsListView extends React.Component { title={i18n('Virtual Function Components')} filterValue={localFilter} placeholder={i18n('Filter Components')} - onFilter={filter => this.setState({localFilter: filter})} - isReadOnlyMode={isReadOnlyMode}> + onFilter={value => this.setState({localFilter: value})} + isReadOnlyMode={isReadOnlyMode} + twoColumns> {this.filterList().map(component => this.renderComponentsListItem(component))} </ListEditorView> ); @@ -52,20 +69,18 @@ class SoftwareProductComponentsListView extends React.Component { renderComponentsListItem(component) { let {id: componentId, name, displayName, description = ''} = component; - let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; + let {currentSoftwareProduct: {id, version}, onComponentSelect} = this.props; return ( <ListEditorItemView key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} className='list-editor-item-view' - onSelect={() => onComponentSelect({id, componentId})}> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Component')}</div> + onSelect={() => onComponentSelect({id, componentId, version})}> + <ListEditorItemViewField> <div className='name'>{displayName}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Description')}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> <div className='description'>{description}</div> - </div> + </ListEditorItemViewField> </ListEditorItemView> ); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js index 7ac1c707ab..e97477b54d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js @@ -1,51 +1,46 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import SoftwareProductComponentComputeView from './SoftwareProductComponentComputeView.jsx'; import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let {componentEditor: {qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); - let minNumberOfVMsSelectedByUser = 0; - if(qdata && qdata.compute && qdata.compute.numOfVMs){ - minNumberOfVMsSelectedByUser = qdata.compute.numOfVMs.minimum || 0; - } - return { qdata, - qschema, - isReadOnlyMode, - minNumberOfVMsSelectedByUser + dataMap, + qgenericFieldInfo, + isReadOnlyMode }; }; -const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + onQDataChanged: (deltaData, customValidations) => ValidationHelper.qDataChanged(dispatch, {deltaData, customValidations: customValidations, + qName: COMPONENTS_QUESTIONNAIRE}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata});}, + qValidateData: (data, customValidations) => ValidationHelper.qValidateData(dispatch, {data, customValidations: customValidations, + qName: COMPONENTS_QUESTIONNAIRE}) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx index 3bad147117..8c197f0d49 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx @@ -1,128 +1,71 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; - +import Form from 'nfvo-components/input/validation/Form.jsx'; +import VmSizing from './computeComponents/VmSizing.jsx'; +import NumberOfVms from './computeComponents/NumberOfVms.jsx'; +import GuestOs from './computeComponents/GuestOs.jsx'; +import Validator from 'nfvo-utils/Validator.js'; class SoftwareProductComponentComputeView extends React.Component { static propTypes = { - qdata: React.PropTypes.object, - qschema: React.PropTypes.object, + dataMap: React.PropTypes.object, + qgenericFieldInfo: React.PropTypes.object, isReadOnlyMode: React.PropTypes.bool, - minNumberOfVMsSelectedByUser: React.PropTypes.number, onQDataChanged: React.PropTypes.func.isRequired, + qValidateData: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired }; render() { - let {qdata, qschema, isReadOnlyMode, minNumberOfVMsSelectedByUser, onQDataChanged, onSubmit} = this.props; + let {qdata, dataMap, qgenericFieldInfo, isReadOnlyMode, onQDataChanged, qValidateData, onSubmit} = this.props; return ( <div className='vsp-component-questionnaire-view'> - <ValidationForm - ref='computeValidationForm' + { qgenericFieldInfo && <Form + ref={ (form) => { this.form = form; }} + formReady={null} + isValid={true} hasButtons={false} onSubmit={() => onSubmit({qdata})} className='component-questionnaire-validation-form' - isReadOnlyMode={isReadOnlyMode} - onDataChanged={onQDataChanged} - data={qdata} - schema={qschema}> - - <div className='section-title'>{i18n('VM Sizing')}</div> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Number of CPUs')} - pointer={'/compute/vmSizing/numOfCPUs'}/> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('File System Size (GB)')} - pointer={'/compute/vmSizing/fileSystemSizeGB'}/> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Persistent Storage/Volume Size (GB)')} - pointer={'/compute/vmSizing/persistentStorageVolumeSize'}/> - </div> - <ValidationInput - type='text' - label={i18n('I/O Operations (per second)')} - pointer={'/compute/vmSizing/IOOperationsPerSec'}/> - </div> - </div> - <div className='section-title'>{i18n('Number of VMs')}</div> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Minimum')} - pointer={'/compute/numOfVMs/minimum'}/> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Maximum')} - pointer={'/compute/numOfVMs/maximum'} - validations={{minValue: minNumberOfVMsSelectedByUser}}/> - </div> - <div className='single-col'> - <ValidationInput - type='select' - label={i18n('CPU Oversubscription Ratio')} - pointer={'/compute/numOfVMs/CpuOverSubscriptionRatio'}/> - </div> - <ValidationInput - type='select' - label={i18n('Memory - RAM')} - pointer={'/compute/numOfVMs/MemoryRAM'}/> - </div> - </div> - - <div className='section-title'>{i18n('Guest OS')}</div> - <div className='rows-section'> - <div className='section-field row-flex-components input-row'> - <div className='two-col'> - <ValidationInput - label={i18n('Guest OS')} - type='text' - pointer={'/compute/guestOS/name'}/> - </div> - <div className='empty-two-col'/> - </div> - <div className='vertical-flex input-row'> - <label key='label' className='control-label'>{i18n('OS Bit Size')}</label> - <div className='radio-options-content-row input-row'> - <ValidationInput - type='radiogroup' - pointer={'/compute/guestOS/bitSize'} - className='radio-field'/> - </div> - </div> - <div className='section-field row-flex-components input-row'> - <div className='two-col'> - <ValidationInput - type='textarea' - label={i18n('Guest OS Tools:')} - pointer={'/compute/guestOS/tools'}/> - </div> - <div className='empty-two-col'/> - </div> - </div> - </ValidationForm> + isReadOnlyMode={isReadOnlyMode} > + <VmSizing onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> + <NumberOfVms onQDataChanged={onQDataChanged} dataMap={dataMap} + qgenericFieldInfo={qgenericFieldInfo} qValidateData={qValidateData} + customValidations={{'compute/numOfVMs/maximum' : this.validateMax, 'compute/numOfVMs/minimum': this.validateMin}} /> + <GuestOs onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} /> + </Form> } </div> ); } save(){ - return this.refs.computeValidationForm.handleFormSubmit(new Event('dummy')); + return this.form.handleFormSubmit(new Event('dummy')); + } + + validateMin(value, state) { + let maxVal = state.dataMap['compute/numOfVMs/maximum']; + return Validator.validateItem(value,maxVal,'maximum'); + } + + validateMax(value, state) { + let minVal = state.dataMap['compute/numOfVMs/minimum']; + return Validator.validateItem(value,minVal,'minimum'); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx new file mode 100644 index 0000000000..7a730d6f94 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/GuestOs.jsx @@ -0,0 +1,76 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const GuestOs = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <div> + <GridSection title={i18n('Guest OS')} > + <GridItem colSpan={2}> + <Input + data-test-id='guestOS-name' + label={i18n('Guest OS')} + type='text' + onChange={(tools) => onQDataChanged({'compute/guestOS/name' : tools})} + isValid={qgenericFieldInfo['compute/guestOS/name'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/name'].errorText} + value={dataMap['compute/guestOS/name']} /> + </GridItem> + <GridItem colSpan={2}/> + <GridItem> + <div className='vertical-flex'> + <label key='label' className='control-label'>{i18n('OS Bit Size')}</label> + <div className='radio-options-content-row'> + {qgenericFieldInfo['compute/guestOS/bitSize'].enum.map(bitSize => ( + <Input + data-test-id='guestOS-bitSize' + type='radio' + key={bitSize.enum} + name={'compute/guestOS/bitSize'} + className='radio-field' + value={bitSize.enum} + label={bitSize.title} + onChange={(bit) => onQDataChanged({'compute/guestOS/bitSize' : Number(bit)})} + isValid={qgenericFieldInfo['compute/guestOS/bitSize'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/bitSize'].errorText} + checked={dataMap['compute/guestOS/bitSize'] === bitSize.enum} /> )) } + </div> + </div> + </GridItem> + <GridItem colSpan={2}/> + <GridItem colSpan={2}> + <Input + data-test-id='guestOS-tools' + type='textarea' + label={i18n('Guest OS Tools:')} + onChange={(tools) => onQDataChanged({'compute/guestOS/tools' : tools})} + isValid={qgenericFieldInfo['compute/guestOS/tools'].isValid} + errorText={qgenericFieldInfo['compute/guestOS/tools'].errorText} + value={dataMap['compute/guestOS/tools']} /> + </GridItem> + </GridSection> + + + </div> + ); +}; + +export default GuestOs; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx new file mode 100644 index 0000000000..efeedc653e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/NumberOfVms.jsx @@ -0,0 +1,94 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + + +const NumberOfVms = ({qgenericFieldInfo, dataMap, onQDataChanged, qValidateData, customValidations}) => { + return( + <GridSection titleClassName='software-product-compute-number-of-vms' title={i18n('NUMBER OF VMs')}> + <GridItem> + <Input + data-test-id='numOfVMs-minimum' + type='number' + label={i18n('Minimum')} + onChange={(tools) => { onQDataChanged({'compute/numOfVMs/minimum' : tools}, customValidations); + qValidateData({'compute/numOfVMs/maximum' : dataMap['compute/numOfVMs/maximum']}, customValidations); } } + isValid={qgenericFieldInfo['compute/numOfVMs/minimum'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/minimum'].errorText} + value={dataMap['compute/numOfVMs/minimum']} /> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-maximum' + type='number' + label={i18n('Maximum')} + onChange={(tools) => { onQDataChanged({'compute/numOfVMs/maximum' : tools}, customValidations); + qValidateData({'compute/numOfVMs/minimum' : dataMap['compute/numOfVMs/minimum']}, customValidations); } } + isValid={qgenericFieldInfo['compute/numOfVMs/maximum'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/maximum'].errorText} + value={dataMap['compute/numOfVMs/maximum']} /> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-CpuOverSubscriptionRatio' + label={i18n('CPU Oversubscription Ratio')} + type='select' + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].errorText} + value={dataMap['compute/numOfVMs/CpuOverSubscriptionRatio']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'compute/numOfVMs/CpuOverSubscriptionRatio' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['compute/numOfVMs/CpuOverSubscriptionRatio'].enum.map(cpuOSR => <option value={cpuOSR.enum} key={cpuOSR.enum}>{cpuOSR.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='numOfVMs-MemoryRAM' + type='select' + label={i18n('Memory - RAM')} + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].isValid} + errorText={qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].errorText} + value={dataMap['compute/numOfVMs/MemoryRAM']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'compute/numOfVMs/MemoryRAM' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['compute/numOfVMs/MemoryRAM'].enum.map(mRAM => <option value={mRAM.enum} key={mRAM.enum}>{mRAM.title}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +NumberOfVms.propTypes = { + minNumberOfVMsSelectedByUser: React.PropTypes.number +}; + +export default NumberOfVms; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx new file mode 100644 index 0000000000..39f84807a2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/computeComponents/VmSizing.jsx @@ -0,0 +1,68 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +const VmSizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={i18n('VM Sizing')}> + <GridItem> + <Input + data-test-id='numOfCPUs' + type='number' + label={i18n('Number of CPUs')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/numOfCPUs' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/numOfCPUs'].errorText} + value={dataMap['compute/vmSizing/numOfCPUs']} /> + </GridItem> + <GridItem> + <Input + data-test-id='fileSystemSizeGB' + type='number' + label={i18n('File System Size (GB)')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/fileSystemSizeGB' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/fileSystemSizeGB'].errorText} + value={dataMap['compute/vmSizing/fileSystemSizeGB']} /> + </GridItem> + <GridItem> + <Input + data-test-id='persistentStorageVolumeSize' + type='number' + label={i18n('Persistent Storage/Volume Size (GB)')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/persistentStorageVolumeSize' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/persistentStorageVolumeSize'].errorText} + value={dataMap['compute/vmSizing/persistentStorageVolumeSize']} /> + </GridItem> + <GridItem> + <Input + data-test-id='IOOperationsPerSec' + type='number' + label={i18n('I/O Operations (per second)')} + onChange={(tools) => onQDataChanged({'compute/vmSizing/IOOperationsPerSec' : tools})} + isValid={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].isValid} + errorText={qgenericFieldInfo['compute/vmSizing/IOOperationsPerSec'].errorText} + value={dataMap['compute/vmSizing/IOOperationsPerSec']} /> + </GridItem> + </GridSection> + ); +}; + +export default VmSizing; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js index e4c330bec8..34374aa7fb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js @@ -1,50 +1,55 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import SoftwareProductComponentsGeneralView from './SoftwareProductComponentsGeneralView.jsx'; import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; + + +import {forms, COMPONENTS_QUESTIONNAIRE} from '../SoftwareProductComponentsConstants.js'; + export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {data: componentData = {} , qdata, qschema}} = softwareProductComponents; - + let {componentEditor: {data: componentData = {} , qdata, qgenericFieldInfo : qGenericFieldInfo, dataMap, genericFieldInfo}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); return { componentData, qdata, - qschema, - isReadOnlyMode + isReadOnlyMode, + genericFieldInfo, + qGenericFieldInfo, + dataMap, + isFormValid }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}), - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.ALL_SPC_FORMS}), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, - {softwareProductId, vspComponentId: componentId, componentData, qdata}); - } + {softwareProductId, version, vspComponentId: componentId, componentData, qdata}); + }, + onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx index 5d11e42cd3..e4595f97d6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx @@ -1,177 +1,285 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +const GeneralSection = ({onDataChanged, displayName, vfcCode, description, isReadOnlyMode, genericFieldInfo}) => ( + <GridSection title={i18n('General')}> + {/* disabled until backend will be ready to implement it + <div className='validation-input-wrapper'> + <div className='form-group'> + <label className='control-label'>{i18n('Name')}</label> + <div>{name}</div> + </div> + </div> + + */} + <GridItem> + <Input + data-test-id='name' + label={i18n('Name')} + value={displayName} + disabled={true} + type='text'/> + <Input + data-test-id='vfcCode' + label={i18n('Naming Code')} + value={vfcCode} + isValid={genericFieldInfo.vfcCode.isValid} + errorText={genericFieldInfo.vfcCode.errorText} + onChange={vfcCode => onDataChanged({vfcCode})} + disabled={isReadOnlyMode} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Description')} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + onChange={description => onDataChanged({description})} + disabled={isReadOnlyMode} + value={description} + groupClassName='multi-line-textarea' + data-test-id='description' + type='textarea'/> + </GridItem> + <GridItem /> + </GridSection> + ); + +const HypervisorSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Hypervisor')}> + <GridItem> + <Input + data-test-id='hypervisor' + label={i18n('Supported Hypervisors')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['general/hypervisor/hypervisor'].isValid} + errorText={qgenericFieldInfo['general/hypervisor/hypervisor'].errorText} + value={dataMap['general/hypervisor/hypervisor']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/hypervisor/hypervisor' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['general/hypervisor/hypervisor'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + <GridItem colSpan={2}> + <Input + data-test-id='drivers' + onChange={(driver) => onQDataChanged({'general/hypervisor/drivers' : driver})} + label={i18n('Hypervisor Drivers')} + type='text' + isValid={qgenericFieldInfo['general/hypervisor/drivers'].isValid} + errorText={qgenericFieldInfo['general/hypervisor/drivers'].errorText} + value={dataMap['general/hypervisor/drivers']}/> + </GridItem> + <GridItem colSpan={3}> + <Input + data-test-id='containerFeaturesDescription' + label={i18n('Describe Container Features')} + type='textarea' + onChange={(containerFeaturesDescription) => onQDataChanged({'general/hypervisor/containerFeaturesDescription' : containerFeaturesDescription})} + isValid={qgenericFieldInfo['general/hypervisor/containerFeaturesDescription'].isValid} + errorText={qgenericFieldInfo['general/hypervisor/containerFeaturesDescription'].errorText} + value={dataMap['general/hypervisor/containerFeaturesDescription']}/> + </GridItem> + </GridSection> +); + +const ImageSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Image')}> + <GridItem> + <Input + data-test-id='format' + label={i18n('Image format')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['general/image/format'].isValid} + errorText={qgenericFieldInfo['general/image/format'].errorText} + value={dataMap['general/image/format']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/image/format' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['general/image/format'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='providedBy' + label={i18n('Image provided by')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['general/image/providedBy'].isValid} + errorText={qgenericFieldInfo['general/image/providedBy'].errorText} + value={dataMap['general/image/providedBy']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'general/image/providedBy' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['general/image/providedBy'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='bootDiskSizePerVM' + onChange={(bootDiskSizePerVM) => onQDataChanged({'general/image/bootDiskSizePerVM' : bootDiskSizePerVM})} + label={i18n('Size of boot disk per VM (GB)')} + type='number' + isValid={qgenericFieldInfo['general/image/bootDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/image/bootDiskSizePerVM'].errorText} + value={dataMap['general/image/bootDiskSizePerVM']}/> + </GridItem> + <GridItem> + <Input + data-test-id='ephemeralDiskSizePerVM' + onChange={(ephemeralDiskSizePerVM) => onQDataChanged({'general/image/ephemeralDiskSizePerVM' : ephemeralDiskSizePerVM})} + label={i18n('Size of ephemeral disk per VM (GB)')} + type='number' + isValid={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].isValid} + errorText={qgenericFieldInfo['general/image/ephemeralDiskSizePerVM'].errorText} + value={dataMap['general/image/ephemeralDiskSizePerVM']}/> + </GridItem> + </GridSection> +); + +const RecoverySection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Recovery')}> + <GridItem> + <Input + data-test-id='pointObjective' + label={i18n('VM Recovery Point Objective (Minutes)')} + type='number' + onChange={(pointObjective) => onQDataChanged({'general/recovery/pointObjective' : pointObjective})} + isValid={qgenericFieldInfo['general/recovery/pointObjective'].isValid} + errorText={qgenericFieldInfo['general/recovery/pointObjective'].errorText} + value={dataMap['general/recovery/pointObjective']}/> + </GridItem> + <GridItem> + <Input + data-test-id='timeObjective' + label={i18n('VM Recovery Time Objective (Minutes)')} + type='number' + onChange={(timeObjective) => onQDataChanged({'general/recovery/timeObjective' : timeObjective})} + isValid={qgenericFieldInfo['general/recovery/timeObjective'].isValid} + errorText={qgenericFieldInfo['general/recovery/timeObjective'].errorText} + value={dataMap['general/recovery/timeObjective']}/> + <div className='empty-two-col' /> + </GridItem> + <GridItem colSpan={2} /> + <GridItem colSpan={2}> + <Input + data-test-id='vmProcessFailuresHandling' + className='textarea' + label={i18n('How are in VM process failures handled?')} + type='textarea' + onChange={(vmProcessFailuresHandling) => onQDataChanged({'general/recovery/vmProcessFailuresHandling' : vmProcessFailuresHandling})} + isValid={qgenericFieldInfo['general/recovery/vmProcessFailuresHandling'].isValid} + errorText={qgenericFieldInfo['general/recovery/vmProcessFailuresHandling'].errorText} + value={dataMap['general/recovery/vmProcessFailuresHandling']}/> + <div className='empty-two-col' /> + + </GridItem> + { + /** disabled until backend will be ready to implement it + <div className='row'> + <div className='col-md-3'> + <Input + label={i18n('VM Recovery Document')} + type='text' + pointer='/general/recovery/VMRecoveryDocument'/> + </div> + </div> + */ + } + </GridSection> +); + +const DNSConfigurationSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('DNS Configuration')}> + <GridItem colSpan={2}> + <Input + data-test-id='dnsConfiguration' + label={i18n('Do you have a need for DNS as a Service? Please describe.')} + type='textarea' + onChange={(dnsConfiguration) => onQDataChanged({'general/dnsConfiguration' : dnsConfiguration})} + isValid={qgenericFieldInfo['general/dnsConfiguration'].isValid} + errorText={qgenericFieldInfo['general/dnsConfiguration'].errorText} + value={dataMap['general/dnsConfiguration']}/> + </GridItem> + </GridSection> +); + +const CloneSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Clone')}> + <GridItem colSpan={2}> + <Input + data-test-id='vmCloneUsage' + label={i18n('Describe VM Clone Use')} + type='textarea' + onChange={(vmCloneUsage) => onQDataChanged({'general/vmCloneUsage' : vmCloneUsage})} + isValid={qgenericFieldInfo['general/vmCloneUsage'].isValid} + errorText={qgenericFieldInfo['general/vmCloneUsage'].errorText} + value={dataMap['general/vmCloneUsage']}/> + </GridItem> + </GridSection> +); class SoftwareProductComponentsGeneralView extends React.Component { render() { - let {qdata, qschema, onQDataChanged, onDataChanged, componentData: {displayName, description}, isReadOnlyMode} = this.props; + let {onQDataChanged, onDataChanged, genericFieldInfo, dataMap, qGenericFieldInfo, componentData: {displayName, vfcCode, description}, isReadOnlyMode} = this.props; return( <div className='vsp-components-general'> <div className='general-data'> - <ValidationForm - isReadOnlyMode={isReadOnlyMode} - hasButtons={false}> - <div className=''> - <h3 className='section-title'>{i18n('General')}</h3> - <div className='rows-section'> - <div className='row-flex-components input-row'> - {/** disabled until backend will be ready to implement it - <div className='validation-input-wrapper'> - <div className='form-group'> - <label className='control-label'>{i18n('Name')}</label> - <div>{name}</div> - </div> - </div> - - */} - <div className='single-col'> - <ValidationInput label={i18n('Name')} value={displayName} disabled={true} type='text'/> - </div> - <div className='two-col'> - <ValidationInput - label={i18n('Description')} - onChange={description => onDataChanged({description})} - disabled={isReadOnlyMode} - value={description} - type='textarea'/> - </div> - <div className='empty-col' /> - </div> - </div> - </div> - </ValidationForm> - { - qschema && - <ValidationForm - onDataChanged={onQDataChanged} - data={qdata} - schema={qschema} + {genericFieldInfo && qGenericFieldInfo && <Form + isValid={this.props.isFormValid} + formReady={null} isReadOnlyMode={isReadOnlyMode} + onValidityChanged={(isValidityData) => this.props.onValidityChanged(isValidityData)} hasButtons={false}> - <h3 className='section-title additional-validation-form'>{i18n('Hypervisor')}</h3> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - label={i18n('Supported Hypervisors')} - type='select' - pointer='/general/hypervisor/hypervisor'/> - </div> - <div className='two-col'> - <ValidationInput - label={i18n('Hypervisor Drivers')} - type='text' - pointer='/general/hypervisor/drivers'/> - </div> - <div className='empty-col' /> - </div> - <div className='row-flex-components input-row'> - <div className='three-col'> - <ValidationInput - label={i18n('Describe Container Features')} - type='textarea' - pointer='/general/hypervisor/containerFeaturesDescription'/> - </div> - <div className='empty-col' /> - </div> - </div> - <h3 className='section-title'>{i18n('Image')}</h3> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - label={i18n('Image format')} - type='select' - pointer='/general/image/format'/> - </div> - <div className='single-col'> - <ValidationInput - label={i18n('Image provided by')} - type='select' - pointer='/general/image/providedBy'/> - </div> - <div className='single-col'> - <ValidationInput - label={i18n('Size of boot disk per VM (GB)')} - type='text' - pointer='/general/image/bootDiskSizePerVM'/> - </div> - <ValidationInput - label={i18n('Size of ephemeral disk per VM (GB)')} - type='text' - pointer='/general/image/ephemeralDiskSizePerVM'/> - </div> - </div> - <h3 className='section-title'>{i18n('Recovery')}</h3> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - label={i18n('VM Recovery Point Objective (Minutes)')} - type='text' - pointer='/general/recovery/pointObjective'/> - </div> - <ValidationInput - label={i18n('VM Recovery Time Objective (Minutes)')} - type='text' - pointer='/general/recovery/timeObjective'/> - <div className='empty-two-col' /> - </div> - - - <div className='row-flex-components input-row'> - <div className='two-col'> - <ValidationInput - className='textarea' - label={i18n('How are in VM process failures handled?')} - type='textarea' - pointer='/general/recovery/vmProcessFailuresHandling'/> - </div> - <div className='empty-two-col' /> - { - /** disabled until backend will be ready to implement it - <div className='row'> - <div className='col-md-3'> - <ValidationInput - label={i18n('VM Recovery Document')} - type='text' - pointer='/general/recovery/VMRecoveryDocument'/> - </div> - </div> - */ - } - </div> - </div> - <h3 className='section-title'>{i18n('DNS Configuration')}</h3> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='two-col'> - <ValidationInput - label={i18n('Do you have a need for DNS as a Service? Please describe.')} - type='textarea' - pointer='/general/dnsConfiguration'/> - </div> - <div className='empty-two-col' /> - </div> - </div> - <h3 className='section-title'>{i18n('Clone')}</h3> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='two-col'> - <ValidationInput - label={i18n('Describe VM Clone Use')} - type='textarea' - pointer='/general/vmCloneUsage'/> - </div> - <div className='empty-two-col' /> - </div> - </div> - </ValidationForm> - } + <GeneralSection + onDataChanged={onDataChanged} + displayName={displayName} + vfcCode={vfcCode} + description={description} + isReadOnlyMode={isReadOnlyMode} + genericFieldInfo={genericFieldInfo}/> + <HypervisorSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <ImageSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <RecoverySection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <DNSConfigurationSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <CloneSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + </Form> } </div> </div> ); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js index 4d4034de5b..0634858219 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js @@ -1,45 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import SoftwareProductComponentLoadBalancingView from './SoftwareProductComponentLoadBalancingRefView.jsx'; import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let {componentEditor: {qdata, qgenericFieldInfo : genericFieldInfo, dataMap}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { qdata, - qschema, + genericFieldInfo, + dataMap, isReadOnlyMode }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, version, vspComponentId: componentId, qdata});} }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx index 1aa2babc12..dc86771400 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -1,11 +1,29 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from'nfvo-components/input/validation/Input.jsx'; -const prefix = '/highAvailabilityAndLoadBalancing/'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const prefix = 'highAvailabilityAndLoadBalancing/'; const pointers = [ { @@ -33,6 +51,31 @@ const pointers = [ } ]; +const TextAreaItem = ({item, toggle, expanded, genericFieldInfo, dataMap, onQDataChanged}) => ( + <GridItem colSpan={3} key={item.key} > + <div className={expanded ? 'title' : 'title add-padding'} + data-test-id={`btn-${item.key}`} + onClick={() => toggle(item.key)}> + <SVGIcon name={expanded ? 'chevron-up' : 'chevron-down'}/> + <span className='title-text'>{i18n(item.description)}</span> + {item.added && <div className='new-line'>{i18n(item.added)}</div>} + </div> + <div className={expanded ? 'collapse in' : 'collapse'}> + <div> + <div> + <Input + data-test-id={`input-${item.key}`} + type='textarea' + isValid={genericFieldInfo[`${prefix}${item.key}`].isValid} + errorText={genericFieldInfo[`${prefix}${item.key}`].errorText} + value={dataMap[`${prefix}${item.key}`]} + onChange={(val) => onQDataChanged({[`${prefix}${item.key}`] : val})} /> + </div> + </div> + </div> + </GridItem> +); + class SoftwareProductComponentLoadBalancingView extends React.Component { static propTypes = { componentId: React.PropTypes.string.isRequired, @@ -46,42 +89,65 @@ class SoftwareProductComponentLoadBalancingView extends React.Component { expanded: {} }; - renderTextAreaItem(item) { - return ( - <div className='question'> - <div className={this.state.expanded[item.key] ? 'title' : 'title add-padding'} - onClick={() => this.toggle(item.key)}> - <FontAwesome name={this.state.expanded[item.key] ? 'chevron-up' : 'chevron-down'}/> - {i18n(item.description)} - {item.added && <div className='new-line'>{i18n(item.added)}</div>} - </div> - <div className={this.state.expanded[item.key] ? 'collapse in' : 'collapse'}> - <div className='row'> - <div className='col-md-9'> - <ValidationInput - type='textarea' - pointer={`${prefix}${item.key}`} - maxLength='1000' /> - </div> - </div> - </div> - </div> - ); - } - render() { - let {qdata, qschema, onQDataChanged, isReadOnlyMode} = this.props; + let {dataMap, genericFieldInfo, onQDataChanged, isReadOnlyMode} = this.props; return ( <div className='vsp-components-load-balancing'> <div className='halb-data'> - <div className='load-balancing-page-title'>{i18n('High Availability & Load Balancing')}</div> - <ValidationForm - onDataChanged={onQDataChanged} - data={qdata} schema={qschema} + { genericFieldInfo && <Form + formReady={null} + isValid={true} + onSubmit={() => this.save()} isReadOnlyMode={isReadOnlyMode} hasButtons={false}> - {pointers.map(pointer => this.renderTextAreaItem(pointer))} - </ValidationForm> + <GridSection title={i18n('High Availability & Load Balancing')}> + <GridItem colSpan={1}> + <Input + data-test-id='input-is-component-mandatory' + label={i18n('Is Component Mandatory')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={genericFieldInfo[`${prefix}isComponentMandatory`].isValid} + errorText={genericFieldInfo[`${prefix}isComponentMandatory`].errorText} + value={dataMap[`${prefix}isComponentMandatory`]} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({[`${prefix}isComponentMandatory`] : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + { genericFieldInfo[`${prefix}isComponentMandatory`].enum.map(isMan => <option value={isMan.enum} key={isMan.enum}>{isMan.title}</option>) } + </Input> + </GridItem> + <GridItem colSpan={3}/> + <GridItem colSpan={1}> + <Input + data-test-id='input-high-availability-mode' + label={i18n('High Availability Mode')} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + isValid={genericFieldInfo[`${prefix}highAvailabilityMode`].isValid} + errorText={genericFieldInfo[`${prefix}highAvailabilityMode`].errorText} + value={dataMap[`${prefix}highAvailabilityMode`]} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({[`${prefix}highAvailabilityMode`] : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {genericFieldInfo[`${prefix}highAvailabilityMode`].enum.map(hmode => <option value={hmode.enum} key={hmode.enum}>{hmode.title}</option>)} + </Input> + </GridItem> + <GridItem colSpan={3}/> + </GridSection> + <GridSection> + {pointers.map(pointer => <TextAreaItem onQDataChanged={onQDataChanged} + genericFieldInfo={genericFieldInfo} dataMap={dataMap} item={pointer} key={pointer.key + 'pKey'} + expanded={this.state.expanded[pointer.key]} toggle={(name)=>{this.toggle(name);}} />)} + </GridSection> + </Form> } </div> </div> ); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js index ed7c5a116a..293e252dca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js @@ -1,27 +1,25 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentsMonitoringView from './SoftwareProductComponentsMonitoringView.jsx'; import SoftwareProductComponentsMonitoringAction from './SoftwareProductComponentsMonitoringActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + export const mapStateToProps = ({softwareProduct}) => { @@ -37,11 +35,12 @@ export const mapStateToProps = ({softwareProduct}) => { }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { onDropMibFileToUpload: (formData, type) => SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, { softwareProductId, + version, componentId, formData, type @@ -49,9 +48,18 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, { softwareProductId, + version, componentId, type - }) + }), + + onFileUploadError: () => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_ERROR, + data: { + title: i18n('Upload Failed'), + msg: i18n('Expected "zip" file. Please check the provided file type.') + } + }), }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js index 3faf571c09..64403faa78 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js @@ -1,80 +1,76 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import i18n from 'nfvo-utils/i18n/i18n.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; import Configuration from 'sdc-app/config/Configuration.js'; import SoftwareProductComponentsMonitoringConstants, {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; const UPLOAD = true; -function baseUrl(vspId, componentId) { +function baseUrl(vspId, version, componentId) { + const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${vspId}/components/${componentId}/monitors`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/components/${componentId}/monitors`; } -function snmpTrapUrl(vspId, componentId, isUpload) { - return `${baseUrl(vspId, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; +function snmpTrapUrl(vspId, version, componentId, isUpload) { + return `${baseUrl(vspId, version, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; } -function snmpPollUrl(vspId, componentId, isUpload) { - return `${baseUrl(vspId, componentId)}/snmp${isUpload ? '/upload' : ''}`; +function snmpPollUrl(vspId, version, componentId, isUpload) { + return `${baseUrl(vspId, version, componentId)}/snmp${isUpload ? '/upload' : ''}`; } let onInvalidFileSizeUpload = (dispatch) => dispatch({ - type: NotificationConstants.NOTIFY_ERROR, + type: modalActionTypes.GLOBAL_MODAL_ERROR, data: { title: i18n('Upload Failed'), msg: i18n('no zip file was uploaded or zip file doesn\'t exist') } }); -let uploadSnmpTrapFile = (dispatch, {softwareProductId, componentId, formData}) => { - RestAPIUtil.create(snmpTrapUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({ +let uploadSnmpTrapFile = (dispatch, {softwareProductId, version, componentId, formData}) => { + RestAPIUtil.post(snmpTrapUrl(softwareProductId, version, componentId, UPLOAD), formData).then(()=> dispatch({ type: actionTypes.SNMP_TRAP_UPLOADED, data: {filename: formData.get('upload').name} })); }; -let uploadSnmpPollFile = (dispatch, {softwareProductId, componentId, formData}) => { - RestAPIUtil.create(snmpPollUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({ +let uploadSnmpPollFile = (dispatch, {softwareProductId, version, componentId, formData}) => { + RestAPIUtil.post(snmpPollUrl(softwareProductId, version, componentId, UPLOAD), formData).then(()=> dispatch({ type: actionTypes.SNMP_POLL_UPLOADED, data: {filename: formData.get('upload').name} })); }; -let deleteSnmpTrapFile = (dispatch, {softwareProductId, componentId}) => { - RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ +let deleteSnmpTrapFile = (dispatch, {softwareProductId, version, componentId}) => { + RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ type: actionTypes.SNMP_TRAP_DELETED })); }; -let deleteSnmpPollFile = (dispatch, {softwareProductId, componentId}) => { - RestAPIUtil.destroy(snmpPollUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ +let deleteSnmpPollFile = (dispatch, {softwareProductId, version, componentId}) => { + RestAPIUtil.destroy(snmpPollUrl(softwareProductId, version, componentId, !UPLOAD)).then(()=> dispatch({ type: actionTypes.SNMP_POLL_DELETED })); }; const SoftwareProductComponentsMonitoringAction = { - fetchExistingFiles(dispatch, {softwareProductId, componentId}){ - RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/snmp`).then(response => + fetchExistingFiles(dispatch, {softwareProductId, version, componentId}){ + RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/snmp`).then(response => dispatch({ type: actionTypes.SNMP_FILES_DATA_CHANGE, data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll} @@ -82,13 +78,13 @@ const SoftwareProductComponentsMonitoringAction = { ); }, - uploadSnmpFile(dispatch, {softwareProductId, componentId, formData, type}){ + uploadSnmpFile(dispatch, {softwareProductId, version, componentId, formData, type}){ if (formData.get('upload').size) { if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - uploadSnmpTrapFile(dispatch, {softwareProductId, componentId, formData}); + uploadSnmpTrapFile(dispatch, {softwareProductId, version, componentId, formData}); } else { - uploadSnmpPollFile(dispatch, {softwareProductId, componentId, formData}); + uploadSnmpPollFile(dispatch, {softwareProductId, version, componentId, formData}); } } else { @@ -96,12 +92,12 @@ const SoftwareProductComponentsMonitoringAction = { } }, - deleteSnmpFile(dispatch, {softwareProductId, componentId, type}){ + deleteSnmpFile(dispatch, {softwareProductId, version, componentId, type}){ if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { - deleteSnmpTrapFile(dispatch, {softwareProductId, componentId}); + deleteSnmpTrapFile(dispatch, {softwareProductId, version, componentId}); } else { - deleteSnmpPollFile(dispatch, {softwareProductId, componentId}); + deleteSnmpPollFile(dispatch, {softwareProductId, version, componentId}); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js index eeececb050..d908d36aaa 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js index 72e0a85b10..54513b9634 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; export default (state = {}, action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx index ca090c5f2f..329cc70353 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component, PropTypes} from 'react'; import Dropzone from 'react-dropzone'; import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; @@ -46,7 +61,7 @@ class SoftwareProductComponentsMonitoringView extends Component { return ( <Dropzone className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`} - onDrop={files => this.handleImport(files, {isReadOnlyMode, type, refAndName})} + onDrop={(acceptedFiles, rejectedFiles) => this.handleImport(acceptedFiles, rejectedFiles, {isReadOnlyMode, type, refAndName})} onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} onDragLeave={() => this.setState({dragging:false})} multiple={false} @@ -70,7 +85,7 @@ class SoftwareProductComponentsMonitoringView extends Component { className={`software-product-landing-view-top-block-col-upl${isReadOnlyMode ? ' disabled' : ''}`}> <div className='drag-text'>{i18n('Drag & drop for upload')}</div> <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={() => this.refs[refAndName].open()}> + <div className='upload-btn primary-btn' data-test-id={`monitoring-upload-${refAndName}`} onClick={() => this.refs[refAndName].open()}> <span className='primary-btn-text'>{i18n('Select file')}</span> </div> </div> @@ -95,17 +110,21 @@ class SoftwareProductComponentsMonitoringView extends Component { } } - handleImport(files, {isReadOnlyMode, type, refAndName}) { + handleImport(files, rejectedFiles, {isReadOnlyMode, type, refAndName}) { if (isReadOnlyMode) { return; } - - this.setState({dragging: false}); - let file = files[0]; - let formData = new FormData(); - formData.append('upload', file); - this.refs[refAndName].value = ''; - this.props.onDropMibFileToUpload(formData, type); + if (files.length > 0) { + this.setState({dragging: false}); + let file = files[0]; + let formData = new FormData(); + formData.append('upload', file); + this.refs[refAndName].value = ''; + this.props.onDropMibFileToUpload(formData, type); + } else if (rejectedFiles.length > 0) { + this.setState({dragging: false}); + this.props.onFileUploadError(); + } } getFileTypeDisplayName(type) { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js index a412456e13..7cf1f0189e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js @@ -1,53 +1,66 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; import SoftwareProductComponentsNICEditorView from './SoftwareProductComponentsNICEditorView.jsx'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; let {network: {nicEditor = {}}} = softwareProductComponents; - let {data, qdata, qschema} = nicEditor; + let {data, qdata, genericFieldInfo, qgenericFieldInfo, dataMap, formReady} = nicEditor; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let protocols = []; + if(qdata && qdata.protocols && qdata.protocols.protocols && qdata.protocols.protocols.length){ + protocols = qdata.protocols.protocols; + } + let {version} = currentSoftwareProduct; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo) && ValidationHelper.checkFormValid(qgenericFieldInfo); return { currentSoftwareProduct, isValidityData, + version, data, qdata, - qschema, - isReadOnlyMode + dataMap, + isFormValid, + formReady, + genericFieldInfo, + qgenericFieldInfo, + isReadOnlyMode, + protocols }; }; const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { return { - onDataChanged: deltaData => SoftwareProductComponentsNetworkActionHelper.updateNICData(dispatch, {deltaData}), - onSubmit: ({data, qdata}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, + formName: forms.NIC_EDIT_FORM}), + onSubmit: ({data, qdata, version}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, version, componentId, data, qdata}), onCancel: () => SoftwareProductComponentsNetworkActionHelper.closeNICEditor(dispatch), - onQDataChanged: ({data}) => SoftwareProductComponentsNetworkActionHelper.updateNICQuestionnaire(dispatch, {data}) + onValidateForm: () => ValidationHelper.validateForm(dispatch, forms.NIC_EDIT_FORM), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: NIC_QUESTIONNAIRE}), }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js index d49f9ccb1e..b3c9fe5d98 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js @@ -1,48 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.NICEditor.OPEN: return { ...state, - data: action.nic + data: action.nic, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [] + } + }, + formName: forms.NIC_EDIT_FORM }; case actionTypes.NICEditor.CLOSE: return {}; - case actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE: - return { - ...state, - qdata: action.payload.qdata || state.qdata, - qschema: action.payload.qschema || state.qschema - }; - case actionTypes.NICEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx index 7393a835dc..aad06c82f0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx @@ -1,321 +1,71 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; - -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Acceptable from './nicEditorComponents/Acceptable.jsx'; +import FlowLength from './nicEditorComponents/FlowLength.jsx'; +import OutFlowTraffic from './nicEditorComponents/OutFlowTraffic.jsx'; +import InFlowTraffic from './nicEditorComponents/InFlowTraffic.jsx'; +import Sizing from './nicEditorComponents/Sizing.jsx'; +import Network from './nicEditorComponents/Network.jsx'; +import IpConfig from './nicEditorComponents/IpConfig.jsx'; +import Protocols from './nicEditorComponents/Protocols.jsx'; +import NameAndPurpose from './nicEditorComponents/NameAndPurpose.jsx'; class SoftwareProductComponentsNetworkEditorView extends React.Component { render() { - let {onCancel, isReadOnlyMode} = this.props; - return ( - <ValidationForm - ref='validationForm' - hasButtons={true} - onSubmit={ () => this.submit() } - onReset={ () => onCancel() } - labledButtons={true} - isReadOnlyMode={isReadOnlyMode} - className='vsp-components-network-editor'> - {this.renderEditorFields()} - </ValidationForm> - ); - } - - renderEditorFields() { - let {data = {}, qdata = {}, qschema = {}, onQDataChanged, onDataChanged, isReadOnlyMode} = this.props; + let {onCancel, onValidateForm, isReadOnlyMode, isFormValid, formReady, data = {}, qgenericFieldInfo, dataMap, onDataChanged, protocols, onQDataChanged} = this.props; let {name, description, networkName} = data; let netWorkValues = [{ enum: networkName, title: networkName }]; - return( - <div className='editor-data'> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Name')} - value={name} - disabled={true} - type='text'/> - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Purpose of NIC')} - value={description} - onChange={description => onDataChanged({description})} - disabled={isReadOnlyMode} - type='textarea'/> - </div> - </div> - <ValidationForm - onDataChanged={onQDataChanged} - data={qdata} - schema={qschema} - isReadOnlyMode={isReadOnlyMode} - hasButtons={false}> - <div className='row'> - <div className='part-title'>{i18n('Protocols')}</div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Protocols')} - type='select' - pointer='/protocols/protocols'/> - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Protocol with Highest Traffic Profile')} - type='select' - pointer='/protocols/protocolWithHighestTrafficProfile'/> - </div> - </div> - <div className='row'> - <div className='part-title'>{i18n('IP Configuration')}</div> - <div className='col-md-3'> - <ValidationInput - label={i18n('IPv4 Required')} - type='checkbox' - pointer='/ipConfiguration/ipv4Required'/> - </div> - <div className='col-md-9'> - <ValidationInput - label={i18n('IPv6 Required')} - type='checkbox' - pointer='/ipConfiguration/ipv6Required'/> - </div> - </div> - </ValidationForm> - <div className='row'> - <div className='part-title'>{i18n('Network')}</div> - <div className='col-md-2'> - <ValidationInput - label={i18n('Internal')} - disabled - checked={true} - className='network-radio disabled' - type='radio'/> - </div> - <div className='col-md-4'> - <ValidationInput - label={i18n('External')} - disabled - checked={false} - className='network-radio disabled' - type='radio'/> - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Network')} - type='select' - disabled={true} - values={netWorkValues}/> - </div> + return ( + <div> + {qgenericFieldInfo && <Form + ref={(form) => { this.form = form; }} + hasButtons={true} + onSubmit={ () => this.submit() } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + isValid={isFormValid} + formReady={formReady} + onValidateForm={() => onValidateForm() } + className='vsp-components-network-editor'> + <div className='editor-data'> + <NameAndPurpose name={name} description={description} onDataChanged={onDataChanged} isReadOnlyMode={isReadOnlyMode}/> + <Protocols protocols={protocols} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <IpConfig dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <Network networkValues={netWorkValues} /> + <Sizing qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <InFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <OutFlowTraffic qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <FlowLength qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> + <Acceptable qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged} /> </div> - <ValidationForm - onDataChanged={onQDataChanged} - data={qdata} - schema={qschema} - isReadOnlyMode={isReadOnlyMode} - hasButtons={false}> - <div className='row'> - <div className='part-title'>{i18n('Sizing')}</div> - <div className='col-md-12'> - <ValidationInput - label={i18n('Describe Quality of Service')} - type='textarea' - pointer='/sizing/describeQualityOfService'/> - </div> - </div> - - <div className='row'> - <div className='part-title'>{i18n('Inflow Traffic per second')}</div> - </div> - - <div className='row'> - <div className='col-md-6'> - <div className='row'> - <div className='part-title-small'>{i18n('Packets')}</div> - </div> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Peak')} - type='text' - pointer='/sizing/inflowTrafficPerSecond/packets/peak'/> - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Avg')} - type='text' - pointer='/sizing/inflowTrafficPerSecond/packets/avg'/> - </div> - </div> - </div> - <div className='col-md-6'> - <div className='row'> - <div className='part-title-small'>{i18n('Bytes')}</div> - </div> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Peak')} - type='text' - pointer='/sizing/inflowTrafficPerSecond/bytes/peak'/> - - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Avg')} - type='text' - pointer='/sizing/inflowTrafficPerSecond/bytes/avg'/> - </div> - </div> - </div> - </div> - - <div className='row'> - <div className='part-title'>{i18n('Outflow Traffic per second')}</div> - </div> - - <div className='row'> - <div className='col-md-6'> - <div className='row'> - <div className='part-title-small'>{i18n('Packets')}</div> - </div> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Peak')} - type='text' - pointer='/sizing/outflowTrafficPerSecond/packets/peak'/> - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Avg')} - type='text' - pointer='/sizing/outflowTrafficPerSecond/packets/avg'/> - - </div> - </div> - </div> - <div className='col-md-6'> - <div className='row'> - <div className='part-title-small'>{i18n('Bytes')}</div> - </div> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Peak')} - type='text' - pointer='/sizing/outflowTrafficPerSecond/bytes/peak'/> - - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Avg')} - type='text' - pointer='/sizing/outflowTrafficPerSecond/bytes/avg'/> - - </div> - </div> - </div> - </div> - - <div className='row'> - <div className='part-title'>{i18n('Flow Length')}</div> - </div> - - <div className='row'> - <div className='col-md-6'> - <div className='row'> - <div className='part-title-small'>{i18n('Packets')}</div> - </div> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Peak')} - type='text' - pointer='/sizing/flowLength/packets/peak'/> - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Avg')} - type='text' - pointer='/sizing/flowLength/packets/avg'/> - </div> - </div> - </div> - <div className='col-md-6'> - <div className='row'> - <div className='part-title-small'>{i18n('Bytes')}</div> - </div> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - label={i18n('Peak')} - type='text' - pointer='/sizing/flowLength/bytes/peak'/> - - </div> - <div className='col-md-6'> - <ValidationInput - label={i18n('Avg')} - type='text' - pointer='/sizing/flowLength/bytes/avg'/> - </div> - </div> - </div> - </div> - - <div className='row'> - <div className='col-md-9'> - <div className='row'> - <div className='part-title-small'>{i18n('Acceptable Jitter')}</div> - </div> - <div className='row'> - <div className='col-md-4'> - <ValidationInput - label={i18n('Min')} - type='text' - pointer='/sizing/acceptableJitter/mean'/> - </div> - <div className='col-md-4'> - <ValidationInput - label={i18n('Max')} - type='text' - pointer='/sizing/acceptableJitter/max'/> - </div> - <div className='col-md-4'> - <ValidationInput - label={i18n('Var')} - type='text' - pointer='/sizing/acceptableJitter/variable'/> - </div> - </div> - </div> - <div className='col-md-3'> - <div className='row'> - <div className='part-title-small'>{i18n('Acceptable Packet Loss %')}</div> - </div> - <div className='row'> - <div className='col-md-12'> - <ValidationInput - label={i18n('In Percent')} - type='text' - pointer='/sizing/acceptablePacketLoss'/> - </div> - </div> - </div> - </div> - </ValidationForm> + </Form> } </div> - ); } + submit() { - let {data, qdata, onSubmit} = this.props; - onSubmit({data, qdata}); + let {data, qdata, onSubmit, version} = this.props; + onSubmit({data, qdata, version}); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js index bc53e1a7af..5cfc88bdc9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js index 8ff6b50189..bc061469b1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js @@ -1,65 +1,60 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {NIC_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js'; -function baseUrl(softwareProductId, componentId) { +function baseUrl(softwareProductId, version, componentId) { + const versionId = version.id; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics`; + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/components/${componentId}/nics`; } -function fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire${versionQuery}`); +function fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${nicId}/questionnaire`); } -function fetchNIC({softwareProductId, componentId, nicId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}${versionQuery}`); +function fetchNIC({softwareProductId, version, componentId, nicId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}/${nicId}`); } -function fetchNICsList({softwareProductId, componentId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +function fetchNICsList({softwareProductId, version, componentId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); } -function saveNIC({softwareProductId, componentId, nic: {id, name, description, networkId}}) { - return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${id}`,{ +function saveNIC({softwareProductId, version, componentId, nic: {id, name, description, networkId}}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${id}`,{ name, description, networkId }); } -function saveNICQuestionnaire({softwareProductId, componentId, nicId, qdata}) { - return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire`, qdata); +function saveNICQuestionnaire({softwareProductId, version, componentId, nicId, qdata}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${nicId}/questionnaire`, qdata); } const SoftwareProductComponentNetworkActionHelper = { - fetchNICsList(dispatch, {softwareProductId, componentId, version}) { - return fetchNICsList({softwareProductId, componentId, version}).then((response) => { + fetchNICsList(dispatch, {softwareProductId, version, componentId}) { + return fetchNICsList({softwareProductId, version, componentId}).then((response) => { dispatch({ type: actionTypes.NIC_LIST_UPDATE, response: response.results @@ -80,43 +75,24 @@ const SoftwareProductComponentNetworkActionHelper = { }); }, - loadNICData({softwareProductId, componentId, nicId, version}) { - return fetchNIC({softwareProductId, componentId, nicId, version}); - }, - - loadNICQuestionnaire(dispatch, {softwareProductId, componentId, nicId, version}) { - return fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}).then((response) => { - dispatch({ - type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, - payload: { - qdata: response.data ? JSON.parse(response.data) : {}, - qschema: JSON.parse(response.schema) - } - }); - }); + loadNICData({softwareProductId, version, componentId, nicId}) { + return fetchNIC({softwareProductId, version, componentId, nicId}); }, - updateNICData(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.NICEditor.DATA_CHANGED, - deltaData - }); - }, - - updateNICQuestionnaire(dispatch, {data}) { - dispatch({ - type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, - payload: { - qdata: data - } + loadNICQuestionnaire(dispatch, {softwareProductId, version, componentId, nicId}) { + return fetchNICQuestionnaire({softwareProductId, version, componentId, nicId}).then((response) => { + ValidationHelper.qDataLoaded(dispatch, {qName: NIC_QUESTIONNAIRE ,response: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + }}); }); }, - saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}) { + saveNICDataAndQuestionnaire(dispatch, {softwareProductId, version, componentId, data, qdata}) { SoftwareProductComponentNetworkActionHelper.closeNICEditor(dispatch); return Promise.all([ - saveNICQuestionnaire({softwareProductId, componentId, nicId: data.id, qdata}), - saveNIC({softwareProductId, componentId, nic: data}).then(() => { + saveNICQuestionnaire({softwareProductId, version, componentId, nicId: data.id, qdata}), + saveNIC({softwareProductId, version, componentId, nic: data}).then(() => { dispatch({ type: actionTypes.NIC_LIST_EDIT, nic: data diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js index 193f4b20b5..39c55d876c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -26,8 +21,8 @@ export const actionTypes = keyMirror({ NICEditor: { OPEN: null, - CLOSE: null, - NIC_QUESTIONNAIRE_UPDATE: null, - DATA_CHANGED: null + CLOSE: null } }); + +export const NIC_QUESTIONNAIRE = 'nic'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js index 9172dc691a..c2bd8ce479 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js @@ -1,51 +1,47 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductComponentsNetworkListView from './SoftwareProductComponentsNetworkListView.jsx'; import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; +import {COMPONENTS_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; - let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, qschema}} = softwareProductComponents; + let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, dataMap, qgenericFieldInfo}} = softwareProductComponents; let {data} = nicEditor; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); let {version} = currentSoftwareProduct; - let manualMode = nicList.length <= 0; let isModalInEditMode = true; return { version, componentData, qdata, - qschema, + dataMap, + qgenericFieldInfo, isValidityData, nicList, isDisplayModal: Boolean(data), isModalInEditMode, - manualMode, isReadOnlyMode }; @@ -53,28 +49,28 @@ export const mapStateToProps = ({softwareProduct}) => { const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onAddNIC: () => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: COMPONENTS_QUESTIONNAIRE}), onEditNicClick: (nic, version) => { Promise.all([ SoftwareProductComponentsNetworkActionHelper.loadNICData({ softwareProductId, + version, componentId, - nicId: nic.id, - version + nicId: nic.id }), SoftwareProductComponentsNetworkActionHelper.loadNICQuestionnaire(dispatch, { softwareProductId, + version, componentId, - nicId: nic.id, - version + nicId: nic.id }) ]).then( ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data}) ); }, - onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, - {softwareProductId, + onSubmit: ({qdata, version}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + {softwareProductId, version, vspComponentId: componentId, qdata}); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx index b3e17ff94b..f715016ba3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -1,10 +1,26 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; -import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; +import Input from'nfvo-components/input/validation/Input.jsx'; import Modal from 'nfvo-components/modal/Modal.jsx'; import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js'; @@ -16,38 +32,56 @@ class SoftwareProductComponentsNetworkView extends React.Component { }; render() { - let {qdata, qschema, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; + let {dataMap, qgenericFieldInfo, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; return( <div className='vsp-components-network'> <div className='network-data'> <div> - <ValidationForm - onDataChanged={onQDataChanged} - data={qdata} - isReadOnlyMode={isReadOnlyMode} - schema={qschema} - hasButtons={false}> +{ qgenericFieldInfo && <Form + formReady={null} + isValid={true} + onSubmit={() => this.save()} + isReadOnlyMode={isReadOnlyMode} + hasButtons={false}> <h3 className='section-title'>{i18n('Network Capacity')}</h3> <div className='rows-section'> - <div className='row-flex-components input-row'> + <div className='row-flex-components'> <div className='single-col'> - <ValidationInput + <Input + data-test-id='protocolWithHighestTrafficProfileAcrossAllNICs' label={i18n('Protocol with Highest Traffic Profile across all NICs')} type='select' - pointer='/network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'/> + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'].isValid} + errorText={qgenericFieldInfo['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'].errorText} + value={dataMap['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + { qgenericFieldInfo['network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'].enum.map(proto => + <option value={proto.enum} key={proto.enum}>{proto.title}</option>) } + </Input> </div> <div className='single-col add-line-break'> - <ValidationInput + <Input + data-test-id='networkTransactionsPerSecond' label={i18n('Network Transactions per Second')} - type='text' - pointer='/network/networkCapacity/networkTransactionsPerSecond'/> + type='number' + onChange={(ntps) => onQDataChanged({'network/networkCapacity/networkTransactionsPerSecond' : ntps})} + isValid={qgenericFieldInfo['network/networkCapacity/networkTransactionsPerSecond'].isValid} + errorText={qgenericFieldInfo['network/networkCapacity/networkTransactionsPerSecond'].errorText} + value={dataMap['network/networkCapacity/networkTransactionsPerSecond']} /> </div> <div className='empty-two-col' /> </div> </div> - </ValidationForm> + </Form> } </div> {this.renderNicList()} </div> @@ -70,18 +104,16 @@ class SoftwareProductComponentsNetworkView extends React.Component { renderNicList() { const {localFilter} = this.state; - let {onAddNIC, manualMode, isReadOnlyMode} = this.props; - let onAdd = manualMode ? onAddNIC : false; + let {isReadOnlyMode} = this.props; return ( <ListEditorView title={i18n('Interfaces')} - plusButtonTitle={i18n('Add NIC')} filterValue={localFilter} placeholder={i18n('Filter NICs by Name')} - onAdd={onAdd} isReadOnlyMode={isReadOnlyMode} - onFilter={filter => this.setState({localFilter: filter})}> - {!manualMode && this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} + onFilter={value => this.setState({localFilter: value})} + twoColumns> + {this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} </ListEditorView> ); } @@ -92,22 +124,22 @@ class SoftwareProductComponentsNetworkView extends React.Component { return ( <ListEditorItemView key={id} - className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode} onSelect={() => onEditNicClick(nic, version)}> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Name')}</div> + <ListEditorItemViewField> <div className='name'>{name}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Purpose of NIC')}</div> - <div className='description'>{description}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Network')}</div> - <div className='artifact-name'>{networkName}</div> - </div> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div className='details'> + <div className='title'>{i18n('Purpose of NIC')}</div> + <div className='description'>{description}</div> + </div> + <div className='details'> + <div className='title'>{i18n('Network')}</div> + <div className='artifact-name'>{networkName}</div> + </div> + </ListEditorItemViewField> </ListEditorItemView> ); @@ -128,8 +160,8 @@ class SoftwareProductComponentsNetworkView extends React.Component { } save() { - let {onSubmit, qdata} = this.props; - return onSubmit({qdata}); + let {onSubmit, qdata, version} = this.props; + return onSubmit({qdata, version}); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Acceptable.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Acceptable.jsx new file mode 100644 index 0000000000..524b95c3ad --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Acceptable.jsx @@ -0,0 +1,75 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Acceptable = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection> + <GridItem colSpan={3}> + <div className='part-title-small packets'>{i18n('Acceptable Jitter')}</div> + </GridItem> + <GridItem> + <div className='part-title-small bytes'>{i18n('Allow Packet Loss')}</div> + </GridItem> + <GridItem> + <Input + label={i18n('Mean')} + type='number' + data-test-id='acceptableJitter-mean' + isValid={qgenericFieldInfo['sizing/acceptableJitter/mean'].isValid} + errorText={qgenericFieldInfo['sizing/acceptableJitter/mean'].errorText} + value={dataMap['sizing/acceptableJitter/mean']} + onChange={val => onQDataChanged({'sizing/acceptableJitter/mean' : val})} /> + </GridItem> + <GridItem> + <Input + label={i18n('Max')} + type='number' + data-test-id='acceptableJitter-max' + isValid={qgenericFieldInfo['sizing/acceptableJitter/max'].isValid} + errorText={qgenericFieldInfo['sizing/acceptableJitter/max'].errorText} + value={dataMap['sizing/acceptableJitter/max']} + onChange={val => onQDataChanged({'sizing/acceptableJitter/max' : val})} /> + </GridItem> + <GridItem> + <Input + label={i18n('Var')} + type='number' + data-test-id='acceptableJitter-variable' + isValid={qgenericFieldInfo['sizing/acceptableJitter/variable'].isValid} + errorText={qgenericFieldInfo['sizing/acceptableJitter/variable'].errorText} + value={dataMap['sizing/acceptableJitter/variable']} + onChange={val => onQDataChanged({'sizing/acceptableJitter/variable' : val})} /> + </GridItem> + <GridItem> + <Input + label={i18n('In Percent')} + type='number' + data-test-id='acceptableJitter-acceptablePacketLoss' + isValid={qgenericFieldInfo['sizing/acceptablePacketLoss'].isValid} + errorText={qgenericFieldInfo['sizing/acceptablePacketLoss'].errorText} + value={dataMap['sizing/acceptablePacketLoss']} + onChange={val => onQDataChanged({'sizing/acceptablePacketLoss' : val})} /> + </GridItem> + </GridSection> + ); +}; + +export default Acceptable; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/FlowLength.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/FlowLength.jsx new file mode 100644 index 0000000000..3745fc7c2e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/FlowLength.jsx @@ -0,0 +1,35 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import PacketsBytes from './PacketsBytes.jsx'; + +const pointers = [ + {label: 'Peak', value: 'sizing/flowLength/packets/peak'}, + {label: 'Avg', value: 'sizing/flowLength/packets/avg'}, + {label: 'Peak', value: 'sizing/flowLength/bytes/peak'}, + {label: 'Avg', value: 'sizing/flowLength/bytes/avg'}, +]; + +const FlowLength = (props) => { + return( + <PacketsBytes {...props} title={i18n('Flow Length')} pointers={pointers}/> + ); +}; + +export default FlowLength; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/InFlowTraffic.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/InFlowTraffic.jsx new file mode 100644 index 0000000000..5476ed90e6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/InFlowTraffic.jsx @@ -0,0 +1,35 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import PacketsBytes from './PacketsBytes.jsx'; + +const pointers = [ + {label: 'Peak', value: 'sizing/inflowTrafficPerSecond/packets/peak'}, + {label: 'Avg', value: 'sizing/inflowTrafficPerSecond/packets/avg'}, + {label: 'Peak', value: 'sizing/inflowTrafficPerSecond/bytes/peak'}, + {label: 'Avg', value: 'sizing/inflowTrafficPerSecond/bytes/avg'}, +]; + +const InFlowTraffic = (props) => { + return( + <PacketsBytes {...props} title={i18n('Inflow Traffic per second')} pointers={pointers}/> + ); +}; + +export default InFlowTraffic; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/IpConfig.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/IpConfig.jsx new file mode 100644 index 0000000000..b3a5d21625 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/IpConfig.jsx @@ -0,0 +1,45 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const IpConfig = ({dataMap, onQDataChanged}) => { + return ( + <GridSection title={i18n('IP Configuration')}> + <GridItem> + <Input + label={i18n('IPv4 Required')} + type='checkbox' + onChange={value => onQDataChanged({'ipConfiguration/ipv4Required' : value})} + data-test-id='ipConfiguration-ipv4Required' + value={dataMap['ipConfiguration/ipv4Required']} /> + </GridItem> + <GridItem> + <Input + label={i18n('IPv6 Required')} + type='checkbox' + data-test-id='ipConfiguration-ipv6Required' + onChange={value => onQDataChanged({'ipConfiguration/ipv6Required' : value})} + value={dataMap['ipConfiguration/ipv6Required']} /> + </GridItem> + </GridSection> + ); +}; + +export default IpConfig; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx new file mode 100644 index 0000000000..3dc153d27f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/NameAndPurpose.jsx @@ -0,0 +1,54 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const NameAndPurpose = ({onDataChanged, isReadOnlyMode, name, description}) => { + + return ( + <GridSection> + <GridItem colSpan={2}> + <Input + label={i18n('Name')} + value={name} + data-test-id='nic-name' + disabled={true} + type='text' /> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Purpose of NIC')} + value={description} + data-test-id='nic-description' + onChange={description => onDataChanged({description})} + disabled={isReadOnlyMode} + type='textarea'/> + </GridItem> + </GridSection> + ); +}; + +NameAndPurpose.PropTypes = { + name: React.PropTypes.string, + description: React.PropTypes.array, + onDataChanged: React.PropTypes.func, + isReadOnlyMode: React.PropTypes.bool, +}; + +export default NameAndPurpose; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx new file mode 100644 index 0000000000..43afdbed3a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Network.jsx @@ -0,0 +1,62 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Network = ({networkValues}) => { + return ( + <GridSection title={i18n('Network')}> + <GridItem> + <Input + label={i18n('Internal')} + disabled + checked={true} + data-test-id='nic-internal' + className='network-radio disabled' + type='radio'/> + </GridItem> + <GridItem> + <Input + label={i18n('External')} + disabled + checked={false} + data-test-id='nic-external' + className='network-radio disabled' + type='radio'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Network')} + data-test-id='nic-network' + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' + disabled={true} > + {networkValues.map(val => <option key={val.enum} value={val.enum}>{val.title}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +Network.PropTypes = { + networkValues: React.PropTypes.array +}; + +export default Network; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/OutFlowTraffic.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/OutFlowTraffic.jsx new file mode 100644 index 0000000000..80a3d1579b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/OutFlowTraffic.jsx @@ -0,0 +1,35 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import PacketsBytes from './PacketsBytes.jsx'; + +const pointers = [ + {label: 'Peak', value: 'sizing/outflowTrafficPerSecond/packets/peak'}, + {label: 'Avg', value: 'sizing/outflowTrafficPerSecond/packets/avg'}, + {label: 'Peak', value: 'sizing/outflowTrafficPerSecond/bytes/peak'}, + {label: 'Avg', value: 'sizing/outflowTrafficPerSecond/bytes/avg'}, +]; + +const OutFlowTraffic = (props) => { + return( + <PacketsBytes {...props} title={i18n('Outflow Traffic per second')} pointers={pointers}/> + ); +}; + +export default OutFlowTraffic; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/PacketsBytes.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/PacketsBytes.jsx new file mode 100644 index 0000000000..d7ee91bd15 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/PacketsBytes.jsx @@ -0,0 +1,65 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const PointerInput = ({label, value, onQDataChanged, qgenericFieldInfo, dataMap}) => { + return ( + <GridItem> + <Input + label={i18n(label)} + type='number' + data-test-id={`${value}`} + isValid={qgenericFieldInfo[value].isValid} + errorText={qgenericFieldInfo[value].errorText} + value={dataMap[value]} + onChange={val => onQDataChanged({[value]: val})} /> + </GridItem> + ); +}; + +PointerInput.PropTypes = { + label: React.PropTypes.string, + value: React.PropTypes.string +}; + +const PacketsBytes = ({title, pointers = [], qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={title}> + <GridItem colSpan={2}> + <div className='part-title-small packets'>{i18n('Packets')}</div> + </GridItem> + <GridItem colSpan={2}> + <div className='part-title-small bytes'>{i18n('Bytes')}</div> + </GridItem> + {pointers.map(pointer => {return (<PointerInput key={pointer.value} label={pointer.label} value={pointer.value} + qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged} dataMap={dataMap} />);})} + </GridSection> + ); +}; + +PacketsBytes.PropTypes = { + title: React.PropTypes.string, + pointers: React.PropTypes.array, + onQDataChanged: React.PropTypes.function, + dataMap: React.PropTypes.object, + qgenericFieldInfo: React.PropTypes.object +}; + +export default PacketsBytes; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Protocols.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Protocols.jsx new file mode 100644 index 0000000000..3e8a9f4e77 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Protocols.jsx @@ -0,0 +1,74 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Protocols = ({protocols, qgenericFieldInfo, dataMap, onQDataChanged}) => { + return ( + <GridSection title={i18n('Protocols')}> + <GridItem colSpan={2}> + <InputOptions + data-test-id='nic-protocols' + label={i18n('Protocols')} + type='select' + isMultiSelect={true} + isValid={qgenericFieldInfo['protocols/protocols'].isValid} + errorText={qgenericFieldInfo['protocols/protocols'].errorText} + onInputChange={()=>{}} + onEnumChange={protocols => { + onQDataChanged({'protocols/protocols' : protocols});} + } + multiSelectedEnum={dataMap['protocols/protocols']} + clearable={false} + values={qgenericFieldInfo['protocols/protocols'].enum}/> + </GridItem> + <GridItem colSpan={2}> + <Input + data-test-id='nic-protocolWithHighestTrafficProfile' + label={i18n('Protocol with Highest Traffic Profile')} + type='select' + groupClassName='bootstrap-input-options' + className='input-options-select' + isValid={qgenericFieldInfo['protocols/protocolWithHighestTrafficProfile'].isValid} + errorText={qgenericFieldInfo['protocols/protocolWithHighestTrafficProfile'].errorText} + value={dataMap['protocols/protocolWithHighestTrafficProfile']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'protocols/protocolWithHighestTrafficProfile' : val});} + }> + {(protocols.length === 0) && + <option key={'You must select protocols first...'} value=''>{i18n('You must select protocols first...')}</option> + } + {protocols.map(protocol => <option key={protocol} value={protocol}>{protocol}</option>)} + </Input> + </GridItem> + </GridSection> + ); +}; + +Protocols.PropTypes = { + protocols: React.PropTypes.array, + onQDataChanged: React.PropTypes.function, + dataMap: React.PropTypes.object, + qgenericFieldInfo: React.PropTypes.object +}; + +export default Protocols; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Sizing.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Sizing.jsx new file mode 100644 index 0000000000..1dd0045f7b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/nicEditorComponents/Sizing.jsx @@ -0,0 +1,39 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +const Sizing = ({qgenericFieldInfo, dataMap, onQDataChanged}) => { + return( + <GridSection title={i18n('Sizing')}> + <GridItem colSpan={4}> + <Input + label={i18n('Describe Quality of Service')} + type='textarea' + data-test-id='sizing-describeQualityOfService' + isValid={qgenericFieldInfo['sizing/describeQualityOfService'].isValid} + errorText={qgenericFieldInfo['sizing/describeQualityOfService'].errorText} + value={dataMap['sizing/describeQualityOfService']} + onChange={val => onQDataChanged({'sizing/describeQualityOfService' : val}) }/> + </GridItem> + </GridSection> + ); +}; + +export default Sizing; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js index d535a34a82..b2133ad5d8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js @@ -1,69 +1,65 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; -function baseUrl(softwareProductId, componentId) { +function baseUrl(softwareProductId, version, componentId) { const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`; + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${version.id}/components/${componentId}/processes`; } -function fetchProcessesList({softwareProductId, componentId, version}) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +function fetchProcessesList({softwareProductId, version, componentId}) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version, componentId)}`); } -function deleteProcess({softwareProductId, componentId, processId}) { - return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId)}/${processId}`); +function deleteProcess({softwareProductId, version, componentId, processId}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version, componentId)}/${processId}`); } -function putProcess({softwareProductId, componentId, process}) { - return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${process.id}`, { +function putProcess({softwareProductId, version, componentId, process}) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version, componentId)}/${process.id}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function postProcess({softwareProductId,componentId, process}) { - return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}`, { +function postProcess({softwareProductId, version, componentId, process}) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version, componentId)}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function uploadFileToProcess({softwareProductId, processId, componentId, formData}) { - return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}/${processId}/upload`, formData); +function uploadFileToProcess({softwareProductId, version, processId, componentId, formData}) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version, componentId)}/${processId}/upload`, formData); } const SoftwareProductComponentProcessesActionHelper = { - fetchProcessesList(dispatch, {softwareProductId, componentId, version}) { + fetchProcessesList(dispatch, {softwareProductId, version, componentId}) { dispatch({ type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, processesList: [] }); - return fetchProcessesList({softwareProductId, componentId, version}).then(response => { + return fetchProcessesList({softwareProductId, version, componentId}).then(response => { dispatch({ type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, processesList: response.results @@ -71,8 +67,8 @@ const SoftwareProductComponentProcessesActionHelper = { }); }, - deleteProcess(dispatch, {process, softwareProductId, componentId}) { - return deleteProcess({softwareProductId, processId:process.id, componentId}).then(() => { + deleteProcess(dispatch, {process, softwareProductId, version, componentId}) { + return deleteProcess({softwareProductId, version, processId:process.id, componentId}).then(() => { dispatch({ type: actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, processId: process.id @@ -81,11 +77,11 @@ const SoftwareProductComponentProcessesActionHelper = { }, - saveProcess(dispatch, {softwareProductId, componentId, previousProcess, process}) { + saveProcess(dispatch, {softwareProductId, version, componentId, previousProcess, process}) { if (previousProcess) { - return putProcess({softwareProductId,componentId, process}).then(() => { + return putProcess({softwareProductId, version, componentId, process}).then(() => { if (process.formData && process.formData.name !== previousProcess.artifactName){ - uploadFileToProcess({softwareProductId, processId: process.id, formData: process.formData, componentId}); + uploadFileToProcess({softwareProductId, version, processId: process.id, formData: process.formData, componentId}); } dispatch({ type: actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, @@ -94,9 +90,9 @@ const SoftwareProductComponentProcessesActionHelper = { }); } else { - return postProcess({softwareProductId, componentId, process}).then(response => { + return postProcess({softwareProductId, version, componentId, process}).then(response => { if (process.formData) { - uploadFileToProcess({softwareProductId, processId: response.value, formData: process.formData, componentId}); + uploadFileToProcess({softwareProductId, version, processId: response.value, formData: process.formData, componentId}); } dispatch({ type: actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, @@ -133,12 +129,6 @@ const SoftwareProductComponentProcessesActionHelper = { dispatch({ type:actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE }); - }, - processEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.processEditor.DATA_CHANGED, - deltaData - }); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js index 78a111a426..d15432b3fb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -27,8 +22,15 @@ export const actionTypes = keyMirror({ SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: null, SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: null, FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES: null, - SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM: null, - processEditor: { - DATA_CHANGED: null - } + SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM: null }); + +export const optionsInputValues = { + PROCESS_TYPE: [ + {title: 'Select...', enum: ''}, + {title: 'Lifecycle Operations', enum: 'Lifecycle_Operations'}, + {title: 'Other', enum: 'Other'} + ] +}; + +export const SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM = 'SOFTWAREPRODUCTPROCESSCOMPONENTSEDITORFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js index 0138023c30..9502e24b1a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js @@ -1,31 +1,29 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper'; import SoftwareProductComponentProcessesEditorView from './SoftwareProductComponentProcessesEditorView.jsx'; +import {SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM} from './SoftwareProductComponentProcessesConstants.js'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductComponents: {componentProcesses = {}}} = softwareProduct; let {processesList = [], processesEditor = {}} = componentProcesses; - let {data} = processesEditor; + let {data, genericFieldInfo, formReady} = processesEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); let previousData; const processId = data ? data.id : null; @@ -35,19 +33,23 @@ const mapStateToProps = ({softwareProduct}) => { return { data, - previousData + genericFieldInfo, + previousData, + isFormValid, + formReady }; }; -const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onDataChanged: deltaData => SoftwareProductComponentProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM}), onCancel: () => SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch), onSubmit: ({previousProcess, process}) => { SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch); - SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, componentId, process}); - } + SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, version, previousProcess, componentId, process}); + }, + onValidateForm: () => ValidationHelper.validateForm(dispatch, SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js index f859f690e8..9afaa6d5fd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js @@ -1,43 +1,53 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; +import {actionTypes, SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM} from './SoftwareProductComponentProcessesConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: return { ...state, + formReady: null, + formName: SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_FORM, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'artifactName' : { + isValid: true, + errorText: '', + validations: [] + }, + 'type' : { + isValid: true, + errorText: '', + validations: [] + } + }, data: action.process }; case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: return {}; - - case actionTypes.processEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx index ca6d843af7..18f2ee129c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx @@ -1,18 +1,48 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Dropzone from 'react-dropzone'; - -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import {optionsInputValues as ComponentProcessesOptionsInputValues} from './SoftwareProductComponentProcessesConstants.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, - artifactName: React.PropTypes.string + artifactName: React.PropTypes.string, + type: React.PropTypes.string }); +const FileUploadBox = ({onClick}) => { + return ( + <div className='file-upload-box'> + <div className='drag-text'>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <div className='upload-btn primary-btn' onClick={onClick}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + ); +}; + class SoftwareProductProcessesEditorView extends React.Component { state = { @@ -30,65 +60,91 @@ class SoftwareProductProcessesEditorView extends React.Component { }; render() { - let {isReadOnlyMode, onCancel, onDataChanged, data = {}} = this.props; - let {name, description, artifactName} = data; + let {isReadOnlyMode, onCancel, onDataChanged, genericFieldInfo, data = {}} = this.props; + let {name, description, artifactName, type} = data; return ( <div> - <ValidationForm + { genericFieldInfo && <Form ref='validationForm' isReadOnlyMode={isReadOnlyMode} hasButtons={true} labledButtons={true} onSubmit={ () => this.submit() } onReset={ () => onCancel() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm() } className='vsp-processes-editor'> - <div className={`vsp-processes-editor-data${isReadOnlyMode ? ' disabled' : '' }`}> - <Dropzone - className={`vsp-process-dropzone-view ${this.state.dragging ? 'active-dragging' : ''}`} - onDrop={files => this.handleImportSubmit(files)} - onDragEnter={() => this.setState({dragging:true})} - onDragLeave={() => this.setState({dragging:false})} - multiple={false} - disableClick={true} - ref='processEditorFileInput' - name='processEditorFileInput' - accept='*.*'> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - onChange={name => onDataChanged({name})} - label={i18n('Name')} - value={name} - validations={{validateName: true, maxLength: 120, required: true}} - type='text'/> - <ValidationInput - label={i18n('Artifacts')} - value={artifactName} - type='text' - disabled/> - </div> - <div className='col-md-6'> - <div className='file-upload-box'> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={() => this.refs.processEditorFileInput.open()}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> - </div> - </div> - <ValidationInput - onChange={description => onDataChanged({description})} - label={i18n('Notes')} - value={description} - name='vsp-process-description' - className='vsp-process-description' - validations={{maxLength: 1000}} - type='textarea'/> - </Dropzone> - </div> - </ValidationForm> + <div className={`vsp-processes-editor-data${isReadOnlyMode ? ' disabled' : '' }`}> + <Dropzone + className={`vsp-process-dropzone-view ${this.state.dragging ? 'active-dragging' : ''}`} + onDrop={(acceptedFiles, rejectedFiles) => this.handleImportSubmit(acceptedFiles, rejectedFiles)} + onDragEnter={() => this.setState({dragging:true})} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput'> + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo.name.isValid} + isRequired={true} + data-test-id='name' + errorText={genericFieldInfo.name.errorText} + label={i18n('Name')} + value={name} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <FileUploadBox onClick={() => this.refs.processEditorFileInput.open()} /> + </GridItem> + </GridSection> + <GridSection> + <GridItem colSpan={2}> + <Input + name='vsp-process-description' + groupClassName='vsp-process-description' + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + label={i18n('Notes')} + value={description} + data-test-id='vsp-process-description' + type='textarea'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Artifacts')} + data-test-id='artifacts' + value={artifactName} + type='text' + disabled/> + <Input + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({type: val});} + } + value={type} + label={i18n('Process Type')} + data-test-id='process-type' + isValid={genericFieldInfo.type.isValid} + errorText={genericFieldInfo.type.errorText} + type='select' + className='input-options-select' + groupClassName='bootstrap-input-options' > + {ComponentProcessesOptionsInputValues.PROCESS_TYPE.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + </GridSection> + </Dropzone> + </div> + </Form>} </div> ); } @@ -110,14 +166,25 @@ class SoftwareProductProcessesEditorView extends React.Component { } - handleImportSubmit(files) { - let {onDataChanged} = this.props; - this.setState({ - dragging: false, - complete: '0', - files - }); - onDataChanged({artifactName: files[0].name}); + handleImportSubmit(files, rejectedFiles) { + if (files.length > 0) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } + else if (rejectedFiles.length > 0) { + this.setState({ + dragging: false + }); + if (DEBUG) { + console.log('file was rejected ' + rejectedFiles[0].name); + } + } + } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js index 5f6932897e..a8cb709194 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js @@ -1,30 +1,27 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; import SoftwareProductComponentsProcessesListView from './SoftwareProductComponentsProcessesListView.jsx'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents: {componentProcesses = {}}} = softwareProduct; let{processesList = [], processesEditor = {}} = componentProcesses; @@ -42,12 +39,19 @@ const mapStateToProps = ({softwareProduct}) => { }; -const mapActionsToProps = (dispatch, {softwareProductId}) => { +const mapActionsToProps = (dispatch, {componentId, softwareProductId}) => { return { onAddProcess: () => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch), onEditProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch, process), - onDeleteProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + onDeleteProcessClick: (process, version) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + onConfirmed: ()=> SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, + {process, softwareProductId, version, componentId}) + } + }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js index 4bb124d52f..98e24a9c21 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx deleted file mode 100644 index 48fa862364..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; - -function renderMsg(processToDelete) { - let name = processToDelete ? processToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); - return ( - <div> - <p>{msg}</p> - </div> - ); -}; - -const mapStateToProps = ({softwareProduct}) => { - let {softwareProductEditor, softwareProductComponents} = softwareProduct; - let {componentProcesses} = softwareProductComponents; - let {processToDelete} = componentProcesses; - let softwareProductId = softwareProductEditor.data.id; - const show = processToDelete !== false; - return { - show, - title: 'Warning!', - type: 'warning', - msg: renderMsg(processToDelete), - confirmationDetails: {processToDelete, softwareProductId} - }; -}; - -const mapActionsToProps = (dispatch,{componentId, softwareProductId}) => { - return { - onConfirmed: ({processToDelete}) => { - SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId, componentId}); - SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch); - }, - onDeclined: () => { - SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx index a8b07e9194..650d6d5ebc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Modal from 'nfvo-components/modal/Modal.jsx'; @@ -6,7 +21,6 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import SoftwareProductProcessesEditor from './SoftwareProductComponentProcessesEditor.js'; -import SoftwareProductComponentsProcessesConfirmationModal from './SoftwareProductComponentsProcessesConfirmationModal.jsx'; class SoftwareProductProcessesView extends React.Component { @@ -22,12 +36,11 @@ class SoftwareProductProcessesView extends React.Component { isModalInEditMode: React.PropTypes.bool, onStorageSelect: React.PropTypes.func, componentId: React.PropTypes.string, - softwareProductId: React.PropTypes.string + softwareProductId: React.PropTypes.string, + currentSoftwareProduct: React.PropTypes.object }; render() { - let { softwareProductId, componentId} = this.props; - return ( <div className='vsp-processes-page'> <div className='software-product-view'> @@ -35,18 +48,15 @@ class SoftwareProductProcessesView extends React.Component { {this.renderEditor()} {this.renderProcessList()} </div> - <SoftwareProductComponentsProcessesConfirmationModal - componentId={componentId} - softwareProductId={softwareProductId}/> </div> </div> ); } renderEditor() { - let {softwareProductId, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {softwareProductId, currentSoftwareProduct: {version}, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; return ( - <Modal show={isDisplayModal} bsSize='large' animation={true}> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal'> <Modal.Header> <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title> </Modal.Header> @@ -54,6 +64,7 @@ class SoftwareProductProcessesView extends React.Component { <SoftwareProductProcessesEditor componentId={componentId} softwareProductId={softwareProductId} + version={version} isReadOnlyMode={isReadOnlyMode}/> </Modal.Body> </Modal> @@ -72,7 +83,8 @@ class SoftwareProductProcessesView extends React.Component { placeholder={i18n('Filter Process')} onAdd={onAddProcess} isReadOnlyMode={isReadOnlyMode} - onFilter={filter => this.setState({localFilter: filter})}> + title={i18n('Process Details')} + onFilter={value => this.setState({localFilter: value})}> {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} </ListEditorView> </div> @@ -81,14 +93,14 @@ class SoftwareProductProcessesView extends React.Component { renderProcessListItem(process, isReadOnlyMode) { let {id, name, description, artifactName = ''} = process; - let {onEditProcessClick, onDeleteProcessClick} = this.props; + let {currentSoftwareProduct: {version}, onEditProcessClick, onDeleteProcessClick} = this.props; return ( <ListEditorItemView key={id} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode} onSelect={() => onEditProcessClick(process)} - onDelete={() => onDeleteProcessClick(process)}> + onDelete={() => onDeleteProcessClick(process, version)}> <div className='list-editor-item-view-field'> <div className='title'>{i18n('Name')}</div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js index fbd3f81ec2..18a3b1e8ff 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js @@ -1,47 +1,48 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; import SoftwareProductComponentStorageView from './SoftwareProductComponentStorageView.jsx'; +import {COMPONENTS_QUESTIONNAIRE} from '../SoftwareProductComponentsConstants.js'; + const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; - let {componentEditor: {data: componentData , qdata, qschema}} = softwareProductComponents; + let {componentEditor: {data: componentData , qdata, qgenericFieldInfo : qGenericFieldInfo, dataMap}} = softwareProductComponents; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); return { componentData, qdata, - qschema, - isReadOnlyMode + isReadOnlyMode, + qGenericFieldInfo, + dataMap }; }; -const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { +const mapActionToProps = (dispatch, {softwareProductId, version, componentId}) => { return { - onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), - onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), + onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + {softwareProductId, version, vspComponentId: componentId, componentData, qdata}); + } }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx index 9c9600c376..28bdf8e5e5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx @@ -1,8 +1,156 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import classnames from 'classnames'; +const BackupSection = ({isReadOnlyMode,dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Backup')}> + <GridItem> + <div className='vertical-flex'> + <label key='label' className={classnames('control-label',{'disabled': isReadOnlyMode})}>{i18n('Backup Type')}</label> + <div className='radio-options-content-row'> + {qgenericFieldInfo['storage/backup/backupType'].enum.map(onSite => ( + <Input + data-test-id='backupType' + type='radio' + key={onSite.enum} + name={'compute/guestOS/bitSize'} + className='radio-field' + value={onSite.enum} + label={onSite.title} + onChange={(site) => onQDataChanged({'storage/backup/backupType' : site})} + isValid={qgenericFieldInfo['storage/backup/backupType'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupType'].errorText} + checked={dataMap['storage/backup/backupType'] === onSite.enum} /> )) } + </div> + </div> + </GridItem> + <GridItem> + <Input + className='section-field' + data-test-id='backupSolution' + onChange={(backupSolution) => onQDataChanged({'storage/backup/backupSolution' : backupSolution})} + label={i18n('Backup Solution')} + type='text' + isValid={qgenericFieldInfo['storage/backup/backupSolution'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupSolution'].errorText} + value={dataMap['storage/backup/backupSolution']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + data-test-id='backupStorageSize' + onChange={(backupStorageSize) => onQDataChanged({'storage/backup/backupStorageSize' : backupStorageSize})} + label={i18n('Backup Storage Size (GB)')} + type='number' + isValid={qgenericFieldInfo['storage/backup/backupStorageSize'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupStorageSize'].errorText} + value={dataMap['storage/backup/backupStorageSize']}/> + </GridItem> + <GridItem> + <Input + data-test-id='backupNIC' + label={i18n('Backup NIC')} + type='select' + className='input-options-select section-field' + groupClassName='bootstrap-input-options' + isValid={qgenericFieldInfo['storage/backup/backupNIC'].isValid} + errorText={qgenericFieldInfo['storage/backup/backupNIC'].errorText} + value={dataMap['storage/backup/backupNIC']} + onChange={(e) => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onQDataChanged({'storage/backup/backupNIC' : val});} + }> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {qgenericFieldInfo['storage/backup/backupNIC'].enum.map(hv => <option value={hv.enum} key={hv.enum}>{hv.title}</option>)} + </Input> + </GridItem> + </GridSection> +); + +const SnapshotBackupSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Snapshot Backup')}> + <GridItem> + <Input + className='section-field' + data-test-id='snapshotFrequency' + onChange={(snapshotFrequency) => onQDataChanged({'storage/snapshotBackup/snapshotFrequency' : snapshotFrequency})} + label={i18n('Backup Storage Size (GB)')} + type='number' + isValid={qgenericFieldInfo['storage/snapshotBackup/snapshotFrequency'].isValid} + errorText={qgenericFieldInfo['storage/snapshotBackup/snapshotFrequency'].errorText} + value={dataMap['storage/snapshotBackup/snapshotFrequency']}/> + </GridItem> + </GridSection> +); + +const LogBackupSection = ({dataMap, onQDataChanged, qgenericFieldInfo}) => ( + <GridSection title={i18n('Log Backup')}> + <GridItem> + <Input + className='section-field' + data-test-id='sizeOfLogFiles' + onChange={(sizeOfLogFiles) => onQDataChanged({'storage/logBackup/sizeOfLogFiles' : sizeOfLogFiles})} + label={i18n('Backup Storage Size (GB)')} + type='number' + isValid={qgenericFieldInfo['storage/logBackup/sizeOfLogFiles'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/sizeOfLogFiles'].errorText} + value={dataMap['storage/logBackup/sizeOfLogFiles']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + label={i18n('Log Retention Period (days)')} + data-test-id='logRetentionPeriod' + onChange={(logRetentionPeriod) => onQDataChanged({'storage/logBackup/logRetentionPeriod' : logRetentionPeriod})} + type='number' + isValid={qgenericFieldInfo['storage/logBackup/logRetentionPeriod'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/logRetentionPeriod'].errorText} + value={dataMap['storage/logBackup/logRetentionPeriod']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + label={i18n('Log Backup Frequency (days)')} + data-test-id='logBackupFrequency' + onChange={(logBackupFrequency) => onQDataChanged({'storage/logBackup/logBackupFrequency' : logBackupFrequency})} + type='number' + isValid={qgenericFieldInfo['storage/logBackup/logBackupFrequency'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/logBackupFrequency'].errorText} + value={dataMap['storage/logBackup/logBackupFrequency']}/> + </GridItem> + <GridItem> + <Input + className='section-field' + label={i18n('Log File Location')} + data-test-id='logFileLocation' + onChange={(logFileLocation) => onQDataChanged({'storage/logBackup/logFileLocation' : logFileLocation})} + type='text' + isValid={qgenericFieldInfo['storage/logBackup/logFileLocation'].isValid} + errorText={qgenericFieldInfo['storage/logBackup/logFileLocation'].errorText} + value={dataMap['storage/logBackup/logFileLocation']}/> + </GridItem> + </GridSection> +); class SoftwareProductComponentStorageView extends React.Component { @@ -14,110 +162,28 @@ class SoftwareProductComponentStorageView extends React.Component { }; render() { - let {qdata, qschema, onQDataChanged, onSubmit, isReadOnlyMode} = this.props; + let {onQDataChanged, dataMap, qGenericFieldInfo, isReadOnlyMode, onSubmit, qdata} = this.props; return( <div className='vsp-component-questionnaire-view'> - <ValidationForm - ref='storageValidationForm' - hasButtons={false} + {qGenericFieldInfo && <Form + ref={form => this.form = form } + isValid={true} + formReady={null} onSubmit={() => onSubmit({qdata})} className='component-questionnaire-validation-form' isReadOnlyMode={isReadOnlyMode} - onDataChanged={onQDataChanged} - data={qdata} - schema={qschema}> - - <div className='section-title'>{i18n('Backup')}</div> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <div className='vertical-flex'> - <label key='label' className='control-label'>{i18n('Backup Type')}</label> - <div className='radio-options-content-row'> - <ValidationInput - label={i18n('On Site')} - type='radiogroup' - pointer={'/storage/backup/backupType'} - className='radio-field'/> - </div> - </div> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Backup Solution')} - pointer={'/storage/backup/backupSolution'} - className='section-field'/> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Backup Storage Size (GB)')} - pointer={'/storage/backup/backupStorageSize'} - className='section-field'/> - </div> - <ValidationInput - type='select' - label={i18n('Backup NIC')} - pointer={'/storage/backup/backupNIC'} - className='section-field'/> - </div> - </div> - - <div className='section-title'>{i18n('Snapshot Backup')}</div> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Snapshot Frequency (hours)')} - pointer={'/storage/snapshotBackup/snapshotFrequency'} - className='section-field'/> - </div> - <div className='empty-two-col' /> - <div className='empty-col' /> - </div> - </div> - - <div className='section-title'>{i18n('Log Backup')}</div> - <div className='rows-section'> - <div className='row-flex-components input-row'> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Size of Log Files (GB)')} - pointer={'/storage/logBackup/sizeOfLogFiles'} - className='section-field'/> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Log Retention Period (days)')} - pointer={'/storage/logBackup/logRetentionPeriod'} - className='section-field'/> - </div> - <div className='single-col'> - <ValidationInput - type='text' - label={i18n('Log Backup Frequency (days)')} - pointer={'/storage/logBackup/logBackupFrequency'} - className='section-field'/> - </div> - <ValidationInput - type='text' - label={i18n('Log File Location')} - pointer={'/storage/logBackup/logFileLocation'} - className='section-field'/> - </div> - </div> - </ValidationForm> + hasButtons={false}> + <BackupSection isReadOnlyMode={isReadOnlyMode} onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <SnapshotBackupSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + <LogBackupSection onQDataChanged={onQDataChanged} dataMap={dataMap} qgenericFieldInfo={qGenericFieldInfo}/> + </Form> } </div> ); } save(){ - return this.refs.storageValidationForm.handleFormSubmit(new Event('dummy')); + return this.form.handleFormSubmit(new Event('dummy')); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js index 46308f0045..19e2d5b0db 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js @@ -1,48 +1,62 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; import SoftwareProductCreationActionHelper from './SoftwareProductCreationActionHelper.js'; import SoftwareProductCreationView from './SoftwareProductCreationView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import SoftwareProductActionHelper from '../SoftwareProductActionHelper.js'; + +export const mapStateToProps = ({finalizedLicenseModelList, softwareProductList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => { + let {genericFieldInfo} = softwareProductCreation; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + let VSPNames = {}; + for (let i = 0; i < softwareProductList.length; i++) { + VSPNames[softwareProductList[i].name] = softwareProductList[i].id; + } -const mapStateToProps = ({finalizedLicenseModelList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => { return { data: softwareProductCreation.data, + selectedVendorId: softwareProductCreation.selectedVendorId, + disableVendor: softwareProductCreation.disableVendor, softwareProductCategories, - finalizedLicenseModelList + finalizedLicenseModelList, + isFormValid, + formReady: softwareProductCreation.formReady, + genericFieldInfo, + VSPNames }; }; -const mapActionsToProps = (dispatch) => { +export const mapActionsToProps = (dispatch) => { return { - onDataChanged: deltaData => SoftwareProductCreationActionHelper.changeData(dispatch, {deltaData}), + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => SoftwareProductCreationActionHelper.resetData(dispatch), onSubmit: (softwareProduct) => { SoftwareProductCreationActionHelper.resetData(dispatch); - SoftwareProductCreationActionHelper.createSoftwareProduct(dispatch, {softwareProduct}).then(softwareProductId => { - let {vendorId: licenseModelId, licensingVersion} = softwareProduct; - OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion}); + SoftwareProductCreationActionHelper.createSoftwareProduct(dispatch, {softwareProduct}).then(response => { + SoftwareProductActionHelper.fetchSoftwareProductList(dispatch).then(() => { + let {vendorId: licenseModelId, licensingVersion} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId: response.vspId, licenseModelId, licensingVersion}); + }); }); - } + }, + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js index f4e51f198e..3b434e3ba4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -1,29 +1,26 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js'; import {actionTypes} from './SoftwareProductCreationConstants.js'; - +import i18n from 'nfvo-utils/i18n/i18n.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -31,7 +28,7 @@ function baseUrl() { } function createSoftwareProduct(softwareProduct) { - return RestAPIUtil.create(baseUrl(), { + return RestAPIUtil.post(baseUrl(), { ...softwareProduct, icon: 'icon', licensingData: {} @@ -40,36 +37,39 @@ function createSoftwareProduct(softwareProduct) { const SoftwareProductCreationActionHelper = { - open(dispatch) { + open(dispatch, vendorId) { SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); dispatch({ - type: actionTypes.OPEN + type: actionTypes.OPEN, + selectedVendorId: vendorId }); + + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_SHOW, + data: { + modalComponentName: modalContentMapper.SOFTWARE_PRODUCT_CREATION, + title: i18n('New Software Product'), + modalComponentProps: { + vendorId + } + } + }); + }, resetData(dispatch) { + dispatch({ - type: actionTypes.RESET_DATA + type: modalActionTypes.GLOBAL_MODAL_CLOSE }); - }, - changeData(dispatch, {deltaData}) { dispatch({ - type: actionTypes.DATA_CHANGED, - deltaData + type: actionTypes.RESET_DATA }); }, createSoftwareProduct(dispatch, {softwareProduct}) { - return createSoftwareProduct(softwareProduct).then(response => { - SoftwareProductActionHelper.addSoftwareProduct(dispatch, { - softwareProduct: { - ...softwareProduct, - id: response.vspId - } - }); - return response.vspId; - }); + return createSoftwareProduct(softwareProduct); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js index 0a9cdb911c..241d7985b1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js @@ -1,27 +1,23 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ OPEN: null, - RESET_DATA: null, - DATA_CHANGED: null + RESET_DATA: null }); + +export const SP_CREATION_FORM_NAME = 'SPCREATIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js index 5e3db09e56..f7a738518e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -1,40 +1,58 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './SoftwareProductCreationConstants.js'; +import {actionTypes, SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.OPEN: return { ...state, - data: {}, - showModal: true - }; - case actionTypes.DATA_CHANGED: - return { - ...state, + formName: SP_CREATION_FORM_NAME, + disableVendor: action.selectedVendorId ? true : false, data: { - ...state.data, - ...action.deltaData - } + vendorId: action.selectedVendorId ? action.selectedVendorId : undefined + }, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'freeEnglishText', data: true}, {type: 'maxLength', data: 1000}, {type: 'required', data: true}] + }, + 'vendorId' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'subCategory' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'category' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 25}, {type: 'validateName', data: true}] + } + }, + showModal: true }; case actionTypes.RESET_DATA: return {}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx index 2c8f243457..11b696855b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx @@ -1,11 +1,28 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import Validator from 'nfvo-utils/Validator.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import {SP_CREATION_FORM_NAME} from './SoftwareProductCreationConstants.js'; +import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; - const SoftwareProductPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, @@ -21,50 +38,66 @@ class SoftwareProductCreationView extends React.Component { data: SoftwareProductPropType, finalizedLicenseModelList: React.PropTypes.array, softwareProductCategories: React.PropTypes.array, + VSPNames: React.PropTypes.object, onDataChanged: React.PropTypes.func.isRequired, onSubmit: React.PropTypes.func.isRequired, onCancel: React.PropTypes.func.isRequired }; render() { - let {softwareProductCategories, data = {}, onDataChanged, onCancel} = this.props; + let {softwareProductCategories, data = {}, onDataChanged, onCancel, genericFieldInfo, disableVendor} = this.props; let {name, description, vendorId, subCategory} = data; const vendorList = this.getVendorList(); - return ( <div className='software-product-creation-page'> - <ValidationForm - ref='validationForm' + { genericFieldInfo && <Form + ref={(validationForm) => this.validationForm = validationForm} hasButtons={true} onSubmit={() => this.submit() } onReset={() => onCancel() } - labledButtons={true}> + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.validate() }> <div className='software-product-form-row'> <div className='software-product-inline-section'> - <ValidationInput + <Input value={name} label={i18n('Name')} - ref='software-product-name' - onChange={name => onDataChanged({name})} - validations={{validateName: true, maxLength: 25, required: true}} + isRequired={true} + onChange={name => onDataChanged({name},SP_CREATION_FORM_NAME, {name: name => this.validateName(name)})} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} type='text' - className='field-section'/> - <ValidationInput - onEnumChange={vendorId => onDataChanged({vendorId})} - value={vendorId} + className='field-section' + data-test-id='new-vsp-name' /> + <Input label={i18n('Vendor')} - values={vendorList} - validations={{required: true}} type='select' - className='field-section'/> - <ValidationInput + value={vendorId} + isRequired={true} + disabled={disableVendor} + onChange={e => this.onSelectVendor(e)} + isValid={genericFieldInfo.vendorId.isValid} + errorText={genericFieldInfo.vendorId.errorText} + className='input-options-select' + groupClassName='bootstrap-input-options' + data-test-id='new-vsp-vendor' > + {vendorList.map(vendor => + <option key={vendor.title} value={vendor.enum}>{vendor.title}</option>)} + </Input> + <Input label={i18n('Category')} type='select' value={subCategory} - onChange={subCategory => this.onSelectSubCategory(subCategory)} - validations={{required: true}} - className='options-input-category'> + isRequired={true} + onChange={e => this.onSelectSubCategory(e)} + isValid={genericFieldInfo.subCategory.isValid} + errorText={genericFieldInfo.subCategory.errorText} + className='input-options-select' + groupClassName='bootstrap-input-options' + data-test-id='new-vsp-category' > <option key='' value=''>{i18n('please select…')}</option> {softwareProductCategories.map(category => category.subcategories && @@ -74,20 +107,23 @@ class SoftwareProductCreationView extends React.Component { <option key={sub.uniqueId} value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)} </optgroup>) } - </ValidationInput> + </Input> </div> <div className='software-product-inline-section'> - <ValidationInput + <Input value={description} label={i18n('Description')} - ref='description' - onChange={description => onDataChanged({description})} - validations={{freeEnglishText: true, maxLength: 1000, required: true}} + isRequired={true} + overlayPos='bottom' + onChange={description => onDataChanged({description},SP_CREATION_FORM_NAME)} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} type='textarea' - className='field-section'/> + className='field-section' + data-test-id='new-vsp-description' /> </div> </div> - </ValidationForm> + </Form>} </div> ); } @@ -95,29 +131,47 @@ class SoftwareProductCreationView extends React.Component { getVendorList() { let {finalizedLicenseModelList} = this.props; - return [{enum: '', title: i18n('please select...')}].concat(finalizedLicenseModelList.map(vendor => { - return { - enum: vendor.id, - title: vendor.vendorName - }; - })); + return [{enum: '', title: i18n('please select...')}].concat( + sortByStringProperty(finalizedLicenseModelList, 'vendorName').map(vendor => { + return { + enum: vendor.id, + title: vendor.vendorName + }; + }) + ); } - onSelectSubCategory(subCategory) { - let {softwareProductCategories, onDataChanged} = this.props; - let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); - onDataChanged({category, subCategory}); + onSelectVendor(e) { + const selectedIndex = e.target.selectedIndex; + const vendorId = e.target.options[selectedIndex].value; + this.props.onDataChanged({vendorId},SP_CREATION_FORM_NAME); } - create(){ - this.refs.validationForm.handleFormSubmit(new Event('dummy')); + onSelectSubCategory(e) { + const selectedIndex = e.target.selectedIndex; + const subCategory = e.target.options[selectedIndex].value; + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory},SP_CREATION_FORM_NAME); } submit() { - const {data:softwareProduct, finalizedLicenseModelList} = this.props; + let {data:softwareProduct, finalizedLicenseModelList} = this.props; softwareProduct.vendorName = finalizedLicenseModelList.find(vendor => vendor.id === softwareProduct.vendorId).vendorName; this.props.onSubmit(softwareProduct); } + + validateName(value) { + const {data: {id}, VSPNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: VSPNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Software product by the name \'' + value + '\' already exists. Software product name must be unique')}; + } + + validate() { + this.props.onValidateForm(SP_CREATION_FORM_NAME); + } } export default SoftwareProductCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependencies.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependencies.js new file mode 100644 index 0000000000..9540d3f869 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependencies.js @@ -0,0 +1,40 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; + +import SoftwareProductDependenciesView from './SoftwareProductDependenciesView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductDependenciesActionHelper from './SoftwareProductDependenciesActionHelper.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductDependencies, softwareProductComponents: {componentsList}} = softwareProduct; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + return { + isReadOnlyMode, + softwareProductDependencies: softwareProductDependencies.length ? softwareProductDependencies : [{sourceId: '', targetId: '', relationType: 'dependsOn', id: 'fake'}], + componentsOptions: componentsList.map(component => ({value: component.id, label: component.name})) + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, version}) => { + return { + onDataChanged: dependenciesList => SoftwareProductDependenciesActionHelper.updateDependencyList(dispatch, {dependenciesList}), + onAddDependency: () => SoftwareProductDependenciesActionHelper.addDependency(dispatch), + onSubmit: (dependenciesList) => SoftwareProductDependenciesActionHelper.saveDependencies(dispatch, {softwareProductId, version, dependenciesList}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductDependenciesView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js new file mode 100644 index 0000000000..e47b33a577 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesActionHelper.js @@ -0,0 +1,58 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes} from './SoftwareProductDependenciesConstants.js'; +import uuid from 'uuid-js'; + +function baseUrl(softwareProductId, version) { + const versionId = version.id; + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/versions/${versionId}/component-dependency-model`; +} + +function fetchDependency(softwareProductId, version) { + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); +} + +function postDependency(softwareProductId, version, dependenciesList) { + let modifedDependencyList = dependenciesList ? dependenciesList.filter(item => item.sourceId && item.targetId) + .map(item => ({sourceId: item.sourceId, targetId: item.targetId, relationType: item.relationType})) : []; + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, {componentDependencyModels:modifedDependencyList}); +} + +const SoftwareProductDependenciesActionHelper = { + updateDependencyList(dispatch, {dependenciesList}) { + dispatch({type: actionTypes.SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE, dependenciesList}); + }, + addDependency(dispatch) { + dispatch({type: actionTypes.ADD_SOFTWARE_PRODUCT_DEPENDENCY}); + }, + fetchDependencies(dispatch, {softwareProductId, version}) { + return fetchDependency(softwareProductId, version).then( response => { + const dependenciesList = response.results ? response.results.map(item => {return {...item, id: uuid.create().toString()};}) : []; + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE, + dependenciesList + }); + }); + }, + saveDependencies(dispatch, {softwareProductId, version, dependenciesList}) { + return postDependency(softwareProductId, version, dependenciesList); + } +}; + +export default SoftwareProductDependenciesActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesConstants.js new file mode 100644 index 0000000000..1f27ed8311 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesConstants.js @@ -0,0 +1,29 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE: null, + ADD_SOFTWARE_PRODUCT_DEPENDENCY: null +}); + +export const relationTypes = { + DEPENDS_ON: 'dependsOn' +}; + +export const relationTypesOptions = [ + {value: relationTypes.DEPENDS_ON, label: 'Depends On'} +]; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesReducer.js new file mode 100644 index 0000000000..3fb479eedc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesReducer.js @@ -0,0 +1,36 @@ + +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {actionTypes, relationTypes} from './SoftwareProductDependenciesConstants.js'; +import {checkCyclesAndMarkDependencies} from './SoftwareProductDependenciesUtils.js'; +import uuid from 'uuid-js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_DEPENDENCIES_LIST_UPDATE: + return checkCyclesAndMarkDependencies(action.dependenciesList); + case actionTypes.ADD_SOFTWARE_PRODUCT_DEPENDENCY: + return [...state, { + sourceId: null, + relationType: relationTypes.DEPENDS_ON, + targetId: null, + id: uuid.create() + }]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesUtils.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesUtils.js new file mode 100644 index 0000000000..94d21bd49d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesUtils.js @@ -0,0 +1,64 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import DirectedGraph from 'nfvo-utils/DirectedGraph.js'; + +function findCycles(graph, node, id, visited = {}, visitedConnections = {}, recursionStack = {}, connectionsWithCycle = {}) { + visited[node] = true; + recursionStack[node] = true; + if (id) { + visitedConnections[id] = true; + } + for (let edge of graph.getEdges(node)) { + if (!visited[edge.target]) { + findCycles(graph, edge.target, edge.id, visited, visitedConnections, recursionStack, connectionsWithCycle); + } else if (recursionStack[edge.target]) { + visitedConnections[edge.id] = true; + for (let connection in visitedConnections) { + connectionsWithCycle[connection] = true; + } + } + } + recursionStack[node] = false; + return {visitedNodes: visited, connectionsWithCycle: connectionsWithCycle}; +} + +export function checkCyclesAndMarkDependencies(dependenciesList) { + let overallVisitedNodes = {}; + let overallConnectionsWithCycles = {}; + + let g = new DirectedGraph(); + for (let dependency of dependenciesList) { + if (dependency.sourceId !== null && dependency.targetId !== null) { + g.addEdge(dependency.sourceId, dependency.targetId, {id: dependency.id}); + } + } + + for (let node in g.nodes) { + if (!overallVisitedNodes.node) { + let {visitedNodes, connectionsWithCycle} = findCycles(g, node, undefined); + overallVisitedNodes = {...overallVisitedNodes, ...visitedNodes}; + overallConnectionsWithCycles = {...overallConnectionsWithCycles, ...connectionsWithCycle}; + } + } + return dependenciesList.map(dependency => ( + { + ...dependency, + hasCycle: dependency.sourceId && dependency.targetId ? + overallConnectionsWithCycles.hasOwnProperty(dependency.id) + : undefined + })); +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx new file mode 100644 index 0000000000..da975a7be2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx @@ -0,0 +1,98 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import SelectActionTable from 'nfvo-components/table/SelectActionTable.jsx'; +import SelectActionTableRow from 'nfvo-components/table/SelectActionTableRow.jsx'; +import SelectActionTableCell from 'nfvo-components/table/SelectActionTableCell.jsx'; +import {relationTypesOptions} from './SoftwareProductDependenciesConstants.js'; + +export default class SoftwareProductDependenciesView extends React.Component { + filterTargets({componentsOptions, sourceToTargetMapping, selectedSourceId, selectedTargetId}) { + let isInMap = sourceToTargetMapping.hasOwnProperty(selectedSourceId); + return componentsOptions.filter(component => { + if (component.value === selectedTargetId) { + return true; + } else { + return component.value !== selectedSourceId && (isInMap ? sourceToTargetMapping[selectedSourceId].indexOf(component.value) < 0 : true); + } + }); + } + + filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId, selectedTargetId}) { + return componentsOptions.filter(component => { + if (component.value === selectedSourceId) { + return true; + } else { + let isInMap = sourceToTargetMapping.hasOwnProperty(component.value); + return component.value !== selectedTargetId && (isInMap ? sourceToTargetMapping[component.value].indexOf(selectedTargetId) < 0 : true); + } + }); + } + + render() { + let {componentsOptions, softwareProductDependencies, onDataChanged, onAddDependency, isReadOnlyMode} = this.props; + let canAdd = softwareProductDependencies.length < componentsOptions.length * (componentsOptions.length - 1); + let sourceToTargetMapping = {}; + softwareProductDependencies.map(dependency => { + let isInMap = sourceToTargetMapping.hasOwnProperty(dependency.sourceId); + if (dependency.targetId) { + sourceToTargetMapping[dependency.sourceId] = isInMap ? [...sourceToTargetMapping[dependency.sourceId], dependency.targetId] : [dependency.targetId]; + } + }); + return ( + <div className='software-product-dependencies'> + <div className='software-product-dependencies-title'>{i18n('Dependencies')}</div> + <SelectActionTable + columns={['Source', 'Relation Type', 'Target']} + isReadOnlyMode={isReadOnlyMode} + onAdd={canAdd ? onAddDependency : undefined} + onAddItem={i18n('Add Rule')}> + {softwareProductDependencies.map(dependency => ( + <SelectActionTableRow + key={dependency.id} + onDelete={() => onDataChanged(softwareProductDependencies.filter(currentDependency => currentDependency.id !== dependency.id))} + overlayMsg={i18n('There is a loop between selections')} + hasError={dependency.hasCycle}> + <SelectActionTableCell + options={this.filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} + selected={dependency.sourceId} + placeholder={i18n('Select VFC...')} + onChange={newSourceId => onDataChanged(softwareProductDependencies.map(currentDependency => + ({...currentDependency, sourceId: currentDependency.id === dependency.id ? newSourceId : currentDependency.sourceId}) + ))} /> + <SelectActionTableCell options={relationTypesOptions} selected={dependency.relationType} clearable={false}/> + <SelectActionTableCell + placeholder={i18n('Select VFC...')} + options={this.filterTargets({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} + selected={dependency.targetId} + onChange={newTargetId => onDataChanged(softwareProductDependencies.map(currentDependency => + ({...currentDependency, targetId: currentDependency.id === dependency.id ? newTargetId : currentDependency.targetId}) + ))} /> + </SelectActionTableRow> + ))} + </SelectActionTable> + </div> + ); + } + + save() { + let {onSubmit, softwareProductDependencies} = this.props; + return onSubmit(softwareProductDependencies); + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js index 16a100c664..ac0282e593 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js @@ -1,35 +1,33 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; import SoftwareProductDetailsView from './SoftwareProductDetailsView.jsx'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, licenseModel: {licenseAgreement, featureGroup}}) => { - let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct; + let {softwareProductEditor: {data: currentSoftwareProduct, genericFieldInfo}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct; let {licensingData = {}, licensingVersion} = currentSoftwareProduct; let licenseAgreementList = [], filteredFeatureGroupsList = []; - if(licensingVersion && licensingVersion !== '') { - licenseAgreementList = licenseAgreement.licenseAgreementList; + licenseAgreementList = licenseAgreement.licenseAgreementList; + if(licensingVersion && licensingVersion !== '' && licensingData && licensingData.licenseAgreement) { let selectedLicenseAgreement = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); if (selectedLicenseAgreement) { let featureGroupsList = featureGroup.featureGroupsList.filter(({referencingLicenseAgreements}) => referencingLicenseAgreements.includes(selectedLicenseAgreement.id)); @@ -38,9 +36,11 @@ export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, lic } } } - let {qdata, qschema} = softwareProductQuestionnaire; + let {qdata, qgenericFieldInfo : qGenericFieldInfo, dataMap} = softwareProductQuestionnaire; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + return { currentSoftwareProduct, softwareProductCategories, @@ -48,16 +48,19 @@ export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, lic featureGroupsList: filteredFeatureGroupsList, finalizedLicenseModelList, qdata, - qschema, - isReadOnlyMode + isReadOnlyMode, + isFormValid, + genericFieldInfo, + qGenericFieldInfo, + dataMap }; }; export const mapActionsToProps = (dispatch) => { return { - onDataChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}), - onVendorParamChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData}), - onQDataChanged: ({data}) => SoftwareProductActionHelper.softwareProductQuestionnaireUpdate(dispatch, {data}), + onDataChanged: (deltaData, formName) => ValidationHelper.dataChanged(dispatch, {deltaData, formName}), + onVendorParamChanged: (deltaData, formName) => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData, formName}), + onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: PRODUCT_QUESTIONNAIRE}), onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}), onSubmit: (softwareProduct, qdata) =>{ return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});} }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js index e060706b37..d62207ff9f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js @@ -1,55 +1,43 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes, forms} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; export default (state = {}, action) => { switch (action.type) { - case actionTypes.softwareProductEditor.OPEN: - return { - ...state, - data: {} - }; - case actionTypes.softwareProductEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; - case actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION: - return { - ...state, - uploadData:action.uploadData - }; case actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED: return { ...state, isValidityData: action.isValidityData }; - case actionTypes.softwareProductEditor.CLOSE: - return {}; case actionTypes.SOFTWARE_PRODUCT_LOADED: return { ...state, + formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'validateName', data: true}, {type: 'maxLength', data: 25}, {type: 'required', data: true}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + } + }, data: action.response }; case actionTypes.TOGGLE_NAVIGATION_ITEM: diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx index 75a5797dec..1d52da38b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx @@ -1,9 +1,266 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component, PropTypes} from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import Form from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import InputOptions from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; +import {forms} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +class GeneralSection extends React.Component { + static propTypes = { + vendorId: PropTypes.string, + name: PropTypes.string, + description: PropTypes.string, + subCategory: PropTypes.string, + softwareProductCategories: PropTypes.array, + finalizedLicenseModelList: PropTypes.array, + onDataChanged: PropTypes.func.isRequired, + onVendorParamChanged: PropTypes.func.isRequired, + onSelectSubCategory: PropTypes.func.isRequired + }; + + onVendorParamChanged(e) { + const selectedIndex = e.target.selectedIndex; + const vendorId = e.target.options[selectedIndex].value; + this.props.onVendorParamChanged({vendorId}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + + } + + onSelectSubCategory(e) { + const selectedIndex = e.target.selectedIndex; + const subCategory = e.target.options[selectedIndex].value; + this.props.onSelectSubCategory(subCategory); + } + + render (){ + let {genericFieldInfo} = this.props; + return ( + <div> + {genericFieldInfo && <GridSection title={i18n('General')}> + <GridItem> + <Input + data-test-id='vsp-name' + label={i18n('Name')} + type='text' + value={this.props.name} + isRequired={true} + errorText={genericFieldInfo.name.errorText} + isValid={genericFieldInfo.name.isValid} + onChange={name => this.props.onDataChanged({name}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS)}/> + <Input + data-test-id='vsp-vendor-name' + label={i18n('Vendor')} + type='select' + value={this.props.vendorId} + onChange={e => this.onVendorParamChanged(e)}> + {sortByStringProperty( + this.props.finalizedLicenseModelList, + 'vendorName' + ).map(lm => <option key={lm.id} value={lm.id}>{lm.vendorName}</option>) + } + </Input> + <Input + data-test-id='vsp-category-name' + label={i18n('Category')} + type='select' + value={this.props.subCategory} + onChange={e => this.onSelectSubCategory(e)}> + { + this.props.softwareProductCategories.map(category => + category.subcategories && + <optgroup + key={category.name} + label={category.name}>{category.subcategories.map(sub => + <option + key={sub.uniqueId} + value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)} + </optgroup> + ) + } + </Input> + </GridItem> + <GridItem colSpan={2} stretch> + <Input + data-test-id='vsp-description' + label={i18n('Description')} + type='textarea' + isRequired={true} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + value={this.props.description} + onChange={description => this.props.onDataChanged({description}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS)}/> + </GridItem> + </GridSection>} + </div>); + } +} +class LicensesSection extends React.Component { + static propTypes = { + onVendorParamChanged: PropTypes.func.isRequired, + vendorId: PropTypes.string, + licensingVersion: PropTypes.object, + licensingVersionsList: PropTypes.array, + licensingData: PropTypes.shape({ + licenceAgreement: PropTypes.string, + featureGroups: PropTypes.array + }), + onFeatureGroupsChanged: PropTypes.func.isRequired, + onLicensingDataChanged: PropTypes.func.isRequired, + featureGroupsList: PropTypes.array, + licenseAgreementList: PropTypes.array + }; + + onVendorParamChanged(e) { + const selectedIndex = e.target.selectedIndex; + const licensingVersion = e.target.options[selectedIndex].value; + this.props.onVendorParamChanged({vendorId: this.props.vendorId, licensingVersion:{id:licensingVersion, label: licensingVersion}}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + } + + onLicensingDataChanged(e) { + const selectedIndex = e.target.selectedIndex; + const licenseAgreement = e.target.options[selectedIndex].value; + this.props.onLicensingDataChanged({licenseAgreement, featureGroups: []}); + } + + render(){ + return ( + <GridSection title={i18n('Licenses')}> + <GridItem> + <Input + data-test-id='vsp-licensing-version' + onChange={e => this.onVendorParamChanged(e)} + value={this.props.licensingVersion ? this.props.licensingVersion.id : ''} + label={i18n('Licensing Version')} + type='select'> + {this.props.licensingVersionsList.map(version => + <option + key={version.enum} + value={version.enum}>{version.title} + </option> + )} + </Input> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-license-agreement' + label={i18n('License Agreement')} + type='select' + value={this.props.licensingData.licenseAgreement ? this.props.licensingData.licenseAgreement : '' } + onChange={(e) => this.onLicensingDataChanged(e)}> + <option key='placeholder' value=''>{i18n('Select...')}</option> + {this.props.licenseAgreementList.map(la => <option value={la.id} key={la.id}>{la.name}</option>)} + </Input> + </GridItem> + <GridItem> + {this.props.licensingData.licenseAgreement && ( + <InputOptions + data-test-id='vsp-feature-group' + type='select' + isMultiSelect={true} + onInputChange={()=>{}} + onEnumChange={featureGroups => this.props.onFeatureGroupsChanged({featureGroups})} + multiSelectedEnum={this.props.licensingData.featureGroups} + name='feature-groups' + label={i18n('Feature Groups')} + clearable={false} + values={this.props.featureGroupsList}/>) + } + </GridItem> + </GridSection> + ); + } +} +const AvailabilitySection = (props) => ( + <GridSection title={i18n('Availability')}> + <GridItem colSpan={2}> + <Input + data-test-id='vsp-use-availability-zone' + label={i18n('Use Availability Zones for High Availability')} + type='checkbox' + value={props.dataMap['general/availability/useAvailabilityZonesForHighAvailability']} + onChange={(aZone) => props.onQDataChanged({'general/availability/useAvailabilityZonesForHighAvailability' : aZone})} /> + </GridItem> + </GridSection> +); +const RegionsSection = (props) => ( + <GridSection title={i18n('Regions')}> + <GridItem> + <InputOptions + data-test-id='vsp-regions' + type='select' + isMultiSelect={true} + onInputChange={()=>{}} + onEnumChange={(regions) => props.onQDataChanged({'general/regionsData/regions' : regions})} + multiSelectedEnum={props.dataMap['general/regionsData/regions']} + name='vsp-regions' + clearable={false} + values={props.genericFieldInfo['general/regionsData/regions'].enum} /> + </GridItem> + </GridSection> +); +const StorageDataReplicationSection = (props) => ( + <GridSection title={i18n('Storage Data Replication')}> + <GridItem> + <Input + data-test-id='vsp-storage-rep-size' + label={i18n('Storage Replication Size (GB)')} + type='number' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationSize'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationSize'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationSize']} + onChange={(sRep) => props.onQDataChanged({'general/storageDataReplication/storageReplicationSize' : sRep})} /> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-storage-rep-source' + label={i18n('Storage Replication Source')} + type='text' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationSource'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationSource'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationSource']} + onChange={(sRepSource) => props.onQDataChanged({'general/storageDataReplication/storageReplicationSource' : sRepSource})} /> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-storage-rep-freq' + label={i18n('Storage Replication Freq. (min)')} + type='number' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationFrequency'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationFrequency'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationFrequency']} + onChange={(sRepFreq) => props.onQDataChanged({'general/storageDataReplication/storageReplicationFrequency' : sRepFreq})} /> + </GridItem> + <GridItem> + <Input + data-test-id='vsp-storage-rep-dest' + label={i18n('Storage Replication Destination')} + type='text' + isValid={props.genericFieldInfo['general/storageDataReplication/storageReplicationDestination'].isValid} + errorText={props.genericFieldInfo['general/storageDataReplication/storageReplicationDestination'].errorText} + value={props.dataMap['general/storageDataReplication/storageReplicationDestination']} + onChange={(sRepDest) => props.onQDataChanged({'general/storageDataReplication/storageReplicationDestination' : sRepDest})} /> + </GridItem> + </GridSection> +); class SoftwareProductDetails extends Component { @@ -17,7 +274,7 @@ class SoftwareProductDetails extends Component { subCategory: PropTypes.string, vendorId: PropTypes.string, vendorName: PropTypes.string, - licensingVersion: PropTypes.string, + licensingVersion: PropTypes.object, licensingData: PropTypes.shape({ licenceAgreement: PropTypes.string, featureGroups: PropTypes.array @@ -31,7 +288,6 @@ class SoftwareProductDetails extends Component { onDataChanged: PropTypes.func.isRequired, onValidityChanged: PropTypes.func.isRequired, qdata: PropTypes.object.isRequired, - qschema: PropTypes.object.isRequired, onQDataChanged: PropTypes.func.isRequired, onVendorParamChanged: PropTypes.func.isRequired }; @@ -40,161 +296,63 @@ class SoftwareProductDetails extends Component { licensingVersionsList: [] }; - render() { - let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, featureGroupsList, licenseAgreementList, currentSoftwareProduct} = this.props; - let {name, description, vendorId, licensingVersion, subCategory, licensingData = {}} = currentSoftwareProduct; + prepareDataForGeneralSection(){ + let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, currentSoftwareProduct, genericFieldInfo} = this.props; + let {name, description, vendorId, subCategory} = currentSoftwareProduct; + return { + name, + description, + vendorId, + subCategory, + softwareProductCategories, + finalizedLicenseModelList, + onDataChanged, + onVendorParamChanged: args => this.onVendorParamChanged(args), + onSelectSubCategory: args => this.onSelectSubCategory(args), + genericFieldInfo + }; + + } + + prepareDataForLicensesSection(){ + let { featureGroupsList, licenseAgreementList, currentSoftwareProduct } = this.props; + let {vendorId, licensingVersion, licensingData = {}} = currentSoftwareProduct; let licensingVersionsList = this.state.licensingVersionsList.length > 0 ? this.state.licensingVersionsList : this.refreshVendorVersionsList(vendorId); - let {qdata, qschema, onQDataChanged} = this.props; + return { + onVendorParamChanged: args => this.onVendorParamChanged(args), + vendorId, + licensingVersion, + licensingVersionsList, + licensingData, + onFeatureGroupsChanged: args => this.onFeatureGroupsChanged(args), + onLicensingDataChanged: args => this.onLicensingDataChanged(args), + featureGroupsList, + licenseAgreementList, + }; + + } + + render() { + let {currentSoftwareProduct} = this.props; + let {qdata, onQDataChanged, dataMap, qGenericFieldInfo} = this.props; let {isReadOnlyMode} = this.props; return ( - <div className='vsp-details-page'> + <div className='vsp-details-page'> <Form - ref='validationForm' + ref={(validationForm) => this.validationForm = validationForm} className='vsp-general-tab' hasButtons={false} + formReady={null} + isValid={this.props.isFormValid} onSubmit={() => this.props.onSubmit(currentSoftwareProduct, qdata)} onValidityChanged={(isValidityData) => this.props.onValidityChanged(isValidityData)} isReadOnlyMode={isReadOnlyMode}> - <div className='section-title general'>{i18n('General')}</div> - <div className='vsp-general-tab-inline-section'> - <div className='vsp-general-tab-sub-section'> - <ValidationInput - label={i18n('Name')} - type='text' - value={name} - onChange={name => onDataChanged({name})} - validations={{validateName: true, maxLength: 120, required: true}} - className='field-section'/> - <ValidationInput - label={i18n('Vendor')} - type='select' - selectedEnum={vendorId} - onEnumChange={vendorId => this.onVendorParamChanged({vendorId})} - className='field-section'> - {finalizedLicenseModelList.map(lm => <option key={lm.id} value={lm.id}>{lm.vendorName}</option>)} - </ValidationInput> - <div className='input-row'> - <ValidationInput - label={i18n('Category')} - type='select' - selectedEnum={subCategory} - onEnumChange={subCategory => this.onSelectSubCategory(subCategory)} - className='field-section'> - { - softwareProductCategories.map(category => - category.subcategories && - <optgroup - key={category.name} - label={category.name}>{category.subcategories.map(sub => - <option - key={sub.uniqueId} - value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)} - </optgroup> - ) - } - </ValidationInput> - </div> - </div> - <div className='vsp-general-tab-sub-section input-row'> - <ValidationInput - label={i18n('Description')} - type='textarea' - value={description} - onChange={description => onDataChanged({description})} - className='field-section' - validations={{required: true}}/> - </div> - </div> - <div className='vsp-general-tab-section licenses'> - <div className='section-title'>{i18n('Licenses')}</div> - <div className='vsp-general-tab-inline-section input-row'> - <ValidationInput - onEnumChange={licensingVersion => this.onVendorParamChanged({vendorId, licensingVersion})} - selectedEnum={licensingVersion} - label={i18n('Licensing Version')} - values={licensingVersionsList} - type='select' - className='field-section'/> - <ValidationInput - label={i18n('License Agreement')} - type='select' - selectedEnum={licensingData.licenseAgreement} - className='field-section' - onEnumChange={(licenseAgreement) => this.onLicensingDataChanged({licenseAgreement, featureGroups: []})}> - <option key='placeholder' value=''>{i18n('Select...')}</option> - {licenseAgreementList.map(la => <option value={la.id} key={la.id}>{la.name}</option>)} - </ValidationInput> - </div> - <div className='vsp-general-tab-inline-section input-row'> - {licensingData.licenseAgreement && ( - <ValidationInput - type='select' - isMultiSelect={true} - onEnumChange={featureGroups => this.onFeatureGroupsChanged({featureGroups})} - multiSelectedEnum={licensingData.featureGroups} - name='feature-groups' - label={i18n('Feature Groups')} - clearable={false} - values={featureGroupsList}/>) - } - </div> - </div> - </Form> - <Form - data={qdata} - schema={qschema} - onDataChanged={onQDataChanged} - className='vsp-general-tab' - hasButtons={false} - isReadOnlyMode={isReadOnlyMode}> - <div className='vsp-general-tab-section'> - <div className='section-title'> {i18n('Availability')} </div> - <div className='vsp-general-tab-inline-section'> - <div className='vsp-general-tab-sub-section input-row'> - <ValidationInput - label={i18n('Use Availability Zones for High Availability')} - type='checkbox' - pointer='/general/availability/useAvailabilityZonesForHighAvailability'/> - </div> - </div> - <div className='section-title'> {i18n('Regions')} </div> - <div className='vsp-general-tab-inline-section'> - <div className='vsp-general-tab-sub-section input-row'> - <ValidationInput - type='select' - laebl='Ziv' - pointer='/general/regionsData/regions'/> - </div> - </div> - <div className='section-title'> {i18n('Storage Data Replication')} </div> - <div className='vsp-general-tab-inline-section'> - <div className='vsp-general-tab-sub-section'> - <ValidationInput - label={i18n('Storage Replication Size (GB)')} - type='text' - pointer='/general/storageDataReplication/storageReplicationSize' - className='field-section'/> - <ValidationInput - label={i18n('Storage Replication Source')} - type='text' - pointer='/general/storageDataReplication/storageReplicationSource' - className='field-section'/> - </div> - <div className='vsp-general-tab-sub-section'> - <ValidationInput - label={i18n('Storage Replication Frequency (minutes)')} - type='text' - pointer='/general/storageDataReplication/storageReplicationFrequency' - className='field-section'/> - <ValidationInput - label={i18n('Storage Replication Destination')} - type='text' - pointer='/general/storageDataReplication/storageReplicationDestination' - className='field-section'/> - </div> - </div> - </div> + <GeneralSection {...this.prepareDataForGeneralSection()}/> + <LicensesSection {...this.prepareDataForLicensesSection()}/> + <AvailabilitySection onQDataChanged={onQDataChanged} dataMap={dataMap} /> + <RegionsSection onQDataChanged={onQDataChanged} dataMap={dataMap} genericFieldInfo={qGenericFieldInfo} /> + <StorageDataReplicationSection onQDataChanged={onQDataChanged} dataMap={dataMap} genericFieldInfo={qGenericFieldInfo} /> </Form> </div> ); @@ -213,7 +371,9 @@ class SoftwareProductDetails extends Component { licensingVersion, licensingData: {} }; - onVendorParamChanged(deltaData); + + onVendorParamChanged(deltaData, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + } refreshVendorVersionsList(vendorId) { @@ -229,20 +389,14 @@ class SoftwareProductDetails extends Component { }]; if(finalVersions) { finalVersions.forEach(version => licensingVersionsList.push({ - enum: version, - title: version + enum: version.id, + title: version.label })); } return licensingVersionsList; } - onSelectSubCategory(subCategory) { - let {softwareProductCategories, onDataChanged} = this.props; - let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); - onDataChanged({category, subCategory}); - } - onFeatureGroupsChanged({featureGroups}) { this.onLicensingDataChanged({featureGroups}); } @@ -253,11 +407,17 @@ class SoftwareProductDetails extends Component { ...this.props.currentSoftwareProduct.licensingData, ...deltaData } - }); + }, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); + } + + onSelectSubCategory(subCategory) { + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory}, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS); } save(){ - return this.refs.validationForm.handleFormSubmit(new Event('dummy')); + return this.validationForm.handleFormSubmit(new Event('dummy')); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js index 7604f5841d..e8091bf8d1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js @@ -1,33 +1,27 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; - import i18n from 'nfvo-utils/i18n/i18n.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; import LandingPageView from './SoftwareProductLandingPageView.jsx'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; -const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { +export const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct; let {licensingData = {}} = currentSoftwareProduct; let {licenseAgreementList} = licenseAgreement; @@ -35,6 +29,8 @@ const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => let licenseAgreementName = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); if (licenseAgreementName) { licenseAgreementName = licenseAgreementName.name; + } else if (licenseAgreementList.length === 0) { // otherwise the state of traingle svgicon will be updated post unmounting + licenseAgreementName = null; } let categoryName = '', subCategoryName = '', fullCategoryDisplayName = ''; @@ -62,27 +58,44 @@ const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => const mapActionsToProps = (dispatch, {version}) => { return { - onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, { + onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId, version}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, { softwareProductId, - licenseModelId + licenseModelId, + version }), - onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}), + onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version}), onUpload: (softwareProductId, formData) => SoftwareProductActionHelper.uploadFile(dispatch, { softwareProductId, formData, - failedNotificationTitle: i18n('Upload validation failed') + failedNotificationTitle: i18n('Upload validation failed'), + version }), + onUploadConfirmation: (softwareProductId, formData) => - SoftwareProductActionHelper.uploadConfirmation(dispatch, { - softwareProductId, - formData, - failedNotificationTitle: i18n('Upload validation failed')}), + dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Upload will erase existing data. Do you want to continue?'), + confirmationButtonText: i18n('Continue'), + title: i18n('Warning'), + onConfirmed: ()=>SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed'), + version + }), + onDeclined: () => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_CLOSE + }) + } + }), onInvalidFileSizeUpload: () => dispatch({ - type: NotificationConstants.NOTIFY_ERROR, + type: modalActionTypes.GLOBAL_MODAL_ERROR, data: { title: i18n('Upload Failed'), + confirmationButtonText: i18n('Continue'), msg: i18n('no zip file was uploaded or zip file doesn\'t exist') } }), diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx deleted file mode 100644 index 4a848834b2..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import {connect} from 'react-redux'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; - -const mapStateToProps = ({softwareProduct}) => { - let {softwareProductEditor} = softwareProduct; - let {uploadData} = softwareProductEditor; - const show = uploadData ? true : false; - return { - show, - title: 'Warning!', - type: 'warning', - msg: i18n('Upload will erase existing data. Do you want to continue?'), - confirmationDetails: {uploadData} - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onConfirmed: ({uploadData}) => { - let {softwareProductId, formData, failedNotificationTitle} = uploadData; - SoftwareProductActionHelper.uploadFile(dispatch, { - softwareProductId, - formData, - failedNotificationTitle - }); - SoftwareProductActionHelper.hideUploadConfirm(dispatch); - }, - onDeclined: () => { - SoftwareProductActionHelper.hideUploadConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx index cf7c7a31a5..5fbf1b74b0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import classnames from 'classnames'; import Dropzone from 'react-dropzone'; @@ -6,15 +21,14 @@ import Dropzone from 'react-dropzone'; import i18n from 'nfvo-utils/i18n/i18n.js'; import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; -import FontAwesome from 'react-fontawesome'; -import SoftwareProductLandingPageUploadConfirmationModal from './SoftwareProductLandingPageUploadConfirmationModal.jsx'; - +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; const SoftwareProductPropType = React.PropTypes.shape({ name: React.PropTypes.string, description: React.PropTypes.string, - version: React.PropTypes.string, + version: React.PropTypes.object, id: React.PropTypes.string, categoryId: React.PropTypes.string, vendorId: React.PropTypes.string, @@ -79,7 +93,6 @@ class SoftwareProductLandingPageView extends React.Component { { componentsList.length > 0 && this.renderComponents() } - <SoftwareProductLandingPageUploadConfirmationModal confirmationButtonText={i18n('Continue')}/> </div> ); } @@ -101,29 +114,31 @@ class SoftwareProductLandingPageView extends React.Component { onClick={() => onDetailsSelect(currentSoftwareProduct)}> <div className='details-container'> <div className='single-detail-section title-section'> - <div> - <div>{name}</div> + <div className='single-detail-section title-text'> + {name} </div> </div> - <div className='multiple-details-section'> - <div className='detail-col' > - <div className='title'>{i18n('Vendor')}</div> - <div className='description'>{vendorName}</div> - </div> - <div className='detail-col'> - <div className='title'>{i18n('Category')}</div> - <div className='description'>{fullCategoryDisplayName}</div> - </div> - <div className='detail-col'> - <div className='title extra-large'>{i18n('License Agreement')}</div> - <div className='description'> - {this.renderLicenseAgreement(licenseAgreementName)} + <div className='details-section'> + <div className='multiple-details-section'> + <div className='detail-col' > + <div className='title'>{i18n('Vendor')}</div> + <div className='description'>{vendorName}</div> + </div> + <div className='detail-col'> + <div className='title'>{i18n('Category')}</div> + <div className='description'>{fullCategoryDisplayName}</div> + </div> + <div className='detail-col'> + <div className='title extra-large'>{i18n('License Agreement')}</div> + <div className='description'> + {this.renderLicenseAgreement(licenseAgreementName)} + </div> </div> </div> - </div> - <div className='single-detail-section'> - <div className='title'>{i18n('Description')}</div> - <div className='description'>{description}</div> + <div className='single-detail-section'> + <div className='title'>{i18n('Description')}</div> + <div className='description'>{description}</div> + </div> </div> </div> </div> @@ -151,19 +166,13 @@ class SoftwareProductLandingPageView extends React.Component { <div className='attachment-details'>{i18n('HEAT Templates')} (<span className='attachment-details-count'>{details.heatTemplates}</span>) </div> - <div className='attachment-details'>{i18n('Images')} (<span - className='attachment-details-count'>{details.images}</span>) - </div> - <div className='attachment-details'>{i18n('Other Artifacts')} (<span - className='attachment-details-count'>{details.otherArtifacts}</span>) - </div> </div> </div> <div className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})}> <div className='drag-text'>{i18n('Drag & drop for upload')}</div> <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}> + <div data-test-id='upload-btn' className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}> <span className='primary-btn-text'>{i18n('Select file')}</span> </div> </div> @@ -180,7 +189,8 @@ class SoftwareProductLandingPageView extends React.Component { title={i18n('Virtual Function Components')} filterValue={localFilter} placeholder={i18n('Filter Components')} - onFilter={filter => this.setState({localFilter: filter})}> + onFilter={value => this.setState({localFilter: value})} + twoColumns> {this.filterList().map(component => this.renderComponentsListItem(component))} </ListEditorView> ); @@ -194,21 +204,19 @@ class SoftwareProductLandingPageView extends React.Component { key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()} className='list-editor-item-view' onSelect={() => onComponentSelect({id, componentId})}> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Component')}</div> + <ListEditorItemViewField> <div className='name'>{displayName}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Description')}</div> + </ListEditorItemViewField> + <ListEditorItemViewField> <div className='description'>{description}</div> - </div> + </ListEditorItemViewField> </ListEditorItemView> ); } renderLicenseAgreement(licenseAgreementName) { - if (!licenseAgreementName) { - return (<FontAwesome name='exclamation-triangle' className='warning-icon'/>); + if (licenseAgreementName !== null && !licenseAgreementName) { + return (<div className='missing-license'><SVGIcon name='exclamation-triangle-full'/><div className='warning-text'>{i18n('Missing')}</div></div>); } return (licenseAgreementName); } @@ -242,6 +250,9 @@ class SoftwareProductLandingPageView extends React.Component { this.startUploading(files); } else { + this.setState({ + dragging: false + }); this.props.onInvalidFileSizeUpload(); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js index dadc7777e1..6161eadba9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; import SoftwareProductNetworksView from './SoftwareProductNetworksView.jsx'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js index d0e29bcfe5..4cb460ecc6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js @@ -1,36 +1,31 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductNetworksConstants.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -function baseUrl(svpId) { +function baseUrl(vspId, version) { + let {id: versionId} = version; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${svpId}/networks`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/networks`; } function fetchNetworksList(softwareProductId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); } const SoftwareProductNetworksActionHelper = { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js index d428d21a26..a3d5578dc4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js index 0c9c62372a..e7c2fcb045 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductNetworksConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx index bd47467fe1..024c5cc44c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx @@ -1,8 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx'; class SoftwareProductNetworksView extends React.Component { @@ -27,7 +43,8 @@ class SoftwareProductNetworksView extends React.Component { title={i18n('Networks')} filterValue={localFilter} placeholder={i18n('Filter Networks')} - onFilter={filter => this.setState({localFilter: filter})}> + onFilter={value => this.setState({localFilter: value})} + twoColumns> {this.filterList().map(network => this.renderNetworksListItem(network))} </ListEditorView> </div> @@ -42,14 +59,15 @@ class SoftwareProductNetworksView extends React.Component { className='list-editor-item-view' isReadOnlyMode={true}> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('Name')}</div> + <ListEditorItemViewField> <div className='name'>{name}</div> - </div> - <div className='list-editor-item-view-field'> - <div className='title'>{i18n('DHCP')}</div> - <div className='artifact-name'>{dhcp ? i18n('YES') : i18n('NO')}</div> - </div> + </ListEditorItemViewField> + <ListEditorItemViewField> + <div className='details'> + <div className='title'>{i18n('DHCP')}</div> + <div className='artifact-name'>{dhcp ? i18n('YES') : i18n('NO')}</div> + </div> + </ListEditorItemViewField> </ListEditorItemView> ); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js index 5c3a8dae01..66926ce4b3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js @@ -1,30 +1,27 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; import SoftwareProductProcessesView from './SoftwareProductProcessesView.jsx'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); let {data} = processesEditor; @@ -42,7 +39,14 @@ const mapActionsToProps = (dispatch, {softwareProductId}) => { return { onAddProcess: () => SoftwareProductProcessesActionHelper.openEditor(dispatch), onEditProcess: (process) => SoftwareProductProcessesActionHelper.openEditor(dispatch, process), - onDeleteProcess: (process) => SoftwareProductProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + onDeleteProcess: (process, version) => dispatch({ + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n('Are you sure you want to delete "{name}"?', {name: process.name}), + onConfirmed: ()=> SoftwareProductProcessesActionHelper.deleteProcess(dispatch, + {process, softwareProductId, version}) + } + }) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js index df5d08ffe5..8fd370b6cc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js @@ -1,62 +1,57 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductProcessesConstants.js'; import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; -function baseUrl(svpId) { +function baseUrl(vspId, version) { + let {id: versionId} = version; const restPrefix = Configuration.get('restPrefix'); - return `${restPrefix}/v1.0/vendor-software-products/${svpId}/processes`; + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${versionId}/processes`; } -function putProcess(softwareProductId, process) { - return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${process.id}`, { +function putProcess(softwareProductId, version, process) { + return RestAPIUtil.put(`${baseUrl(softwareProductId, version)}/${process.id}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function postProcess(softwareProductId, process) { - return RestAPIUtil.create(`${baseUrl(softwareProductId)}`, { +function postProcess(softwareProductId, version, process) { + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}`, { name: process.name, - description: process.description + description: process.description, + type: process.type === '' ? null : process.type }); } -function deleteProcess(softwareProductId, processId) { - return RestAPIUtil.destroy(`${baseUrl(softwareProductId)}/${processId}`); +function deleteProcess(softwareProductId, version, processId) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, version)}/${processId}`); } -function uploadFileToProcess(softwareProductId, processId, formData) +function uploadFileToProcess(softwareProductId, version, processId, formData) { - return RestAPIUtil.create(`${baseUrl(softwareProductId)}/${processId}/upload`, formData); + return RestAPIUtil.post(`${baseUrl(softwareProductId, version)}/${processId}/upload`, formData); } function fetchProcesses(softwareProductId, version) { - let versionQuery = version ? `?version=${version}` : ''; - return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, version)}`); } - - const SoftwareProductActionHelper = { fetchProcessesList(dispatch, {softwareProductId, version}) { @@ -80,8 +75,8 @@ const SoftwareProductActionHelper = { }); }, - deleteProcess(dispatch, {process, softwareProductId}) { - return deleteProcess(softwareProductId, process.id).then(() => { + deleteProcess(dispatch, {process, softwareProductId, version}) { + return deleteProcess(softwareProductId, version, process.id).then(() => { dispatch({ type: actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS, processId: process.id @@ -96,18 +91,11 @@ const SoftwareProductActionHelper = { }); }, - processEditorDataChanged(dispatch, {deltaData}) { - dispatch({ - type: actionTypes.processEditor.DATA_CHANGED, - deltaData - }); - }, - - saveProcess(dispatch, {softwareProductId, previousProcess, process}) { + saveProcess(dispatch, {softwareProductId, version, previousProcess, process}) { if (previousProcess) { - return putProcess(softwareProductId, process).then(() => { + return putProcess(softwareProductId, version, process).then(() => { if (process.formData){ - uploadFileToProcess(softwareProductId, process.id, process.formData); + uploadFileToProcess(softwareProductId, version, process.id, process.formData); } dispatch({ type: actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS, @@ -116,9 +104,9 @@ const SoftwareProductActionHelper = { }); } else { - return postProcess(softwareProductId, process).then(response => { + return postProcess(softwareProductId, version, process).then(response => { if (process.formData) { - uploadFileToProcess(softwareProductId, response.value, process.formData); + uploadFileToProcess(softwareProductId, version, response.value, process.formData); } dispatch({ type: actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx deleted file mode 100644 index 0159352dae..0000000000 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import {connect} from 'react-redux'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; -import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; - -function renderMsg(processToDelete) { - let name = processToDelete ? processToDelete.name : ''; - let msg = i18n('Are you sure you want to delete "{name}"?', {name}); - return ( - <div> - <p>{msg}</p> - </div> - ); -}; - -const mapStateToProps = ({softwareProduct}) => { - let {softwareProductEditor, softwareProductProcesses} = softwareProduct; - let {processToDelete} = softwareProductProcesses; - let softwareProductId = softwareProductEditor.data.id; - - const show = processToDelete !== false; - return { - show, - title: i18n('Warning!'), - type: 'warning', - msg: renderMsg(processToDelete), - confirmationDetails: {processToDelete, softwareProductId} - }; -}; - -const mapActionsToProps = (dispatch) => { - return { - onConfirmed: ({processToDelete, softwareProductId}) => { - SoftwareProductProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId}); - SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch); - }, - onDeclined: () => { - SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch); - } - }; -}; - -export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); - diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js index 63f3067a89..6eee24cdde 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ @@ -27,8 +22,14 @@ export const actionTypes = keyMirror({ SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: null, SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: null, FETCH_SOFTWARE_PRODUCT_PROCESSES: null, - SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM: null, - processEditor: { - DATA_CHANGED: null - } + SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM: null }); + +export const optionsInputValues = { + PROCESS_TYPE: [ + {title: 'Select...', enum: ''}, + {title: 'Other', enum: 'Other'} + ] +}; + +export const VSP_PROCESS_FORM = 'VSPPROCESSFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js index 8dc48c50b1..ff787c357e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js @@ -1,31 +1,28 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper'; import SoftwareProductProcessesEditorView from './SoftwareProductProcessesEditorView.jsx'; +import {VSP_PROCESS_FORM} from './SoftwareProductProcessesConstants.js'; -const mapStateToProps = ({softwareProduct}) => { +export const mapStateToProps = ({softwareProduct}) => { let {softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; - let {data} = processesEditor; - + let {data, genericFieldInfo, formReady} = processesEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); let previousData; const processId = data ? data.id : null; if(processId) { @@ -34,18 +31,22 @@ const mapStateToProps = ({softwareProduct}) => { return { data, - previousData + genericFieldInfo, + previousData, + isFormValid, + formReady }; }; -const mapActionsToProps = (dispatch, {softwareProductId}) => { +const mapActionsToProps = (dispatch, {softwareProductId, version}) => { return { - onDataChanged: deltaData => SoftwareProductProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: VSP_PROCESS_FORM}), onSubmit: ({previousProcess, process}) => { SoftwareProductProcessesActionHelper.closeEditor(dispatch); - SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, process}); + SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, version, previousProcess, process}); }, - onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch) + onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch), + onValidateForm: () => ValidationHelper.validateForm(dispatch, VSP_PROCESS_FORM) }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js index cae25e2c89..11b89b17c2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js @@ -1,43 +1,54 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - -import {actionTypes} from './SoftwareProductProcessesConstants.js'; +import {actionTypes, VSP_PROCESS_FORM} from './SoftwareProductProcessesConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: return { ...state, + formReady: null, + formName: VSP_PROCESS_FORM, + genericFieldInfo: { + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'artifactName' : { + isValid: true, + errorText: '', + validations: [] + }, + 'type' : { + isValid: true, + errorText: '', + validations: [] + } + }, data: action.process }; case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: return {}; - case actionTypes.processEditor.DATA_CHANGED: - return { - ...state, - data: { - ...state.data, - ...action.deltaData - } - }; default: return state; } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx index c2c4aff382..137e4a2b4e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx @@ -1,18 +1,50 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import Dropzone from 'react-dropzone'; import classnames from 'classnames'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import {optionsInputValues as ProcessesOptionsInputValues} from './SoftwareProductProcessesConstants.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, - artifactName: React.PropTypes.string + artifactName: React.PropTypes.string, + type: React.PropTypes.string }); +const FileUploadBox = ({onClick}) => { + return ( + <div className='file-upload-box'> + <div className='drag-text'>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <div className='upload-btn primary-btn' onClick={onClick}> + <span className='primary-btn-text'>{i18n('Select file')}</span> + </div> + </div> + ); +}; + + class SoftwareProductProcessesEditorView extends React.Component { state = { @@ -30,63 +62,89 @@ class SoftwareProductProcessesEditorView extends React.Component { }; render() { - let {data = {}, isReadOnlyMode, onDataChanged, onClose} = this.props; - let {name, description, artifactName} = data; + let {data = {}, isReadOnlyMode, onDataChanged, onClose, genericFieldInfo} = this.props; + let {name, description, artifactName, type} = data; + return ( - <ValidationForm - ref='validationForm' - hasButtons={true} - labledButtons={true} - isReadOnlyMode={isReadOnlyMode} - onSubmit={ () => this.submit() } - onReset={ () => onClose() } - className='vsp-processes-editor'> - <div className={classnames('vsp-processes-editor-data', {'disabled': isReadOnlyMode})}> - <Dropzone - className={classnames('vsp-process-dropzone-view', {'active-dragging': this.state.dragging})} - onDrop={files => this.handleImportSubmit(files)} - onDragEnter={() => this.setState({dragging: true})} - onDragLeave={() => this.setState({dragging: false})} - multiple={false} - disableClick={true} - ref='processEditorFileInput' - name='processEditorFileInput' - accept='*.*'> - <div className='row'> - <div className='col-md-6'> - <ValidationInput - onChange={name => onDataChanged({name})} - label={i18n('Name')} - value={name} - validations={{validateName: true, maxLength: 120, required: true}} - type='text'/> - <ValidationInput - label={i18n('Artifacts')} - value={artifactName} - type='text' - disabled/> - </div> - <div className='col-md-6'> - <div className='file-upload-box'> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={() => this.refs.processEditorFileInput.open()}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> - </div> - </div> - <ValidationInput - onChange={description => onDataChanged({description})} - label={i18n('Notes')} - value={description} - name='vsp-process-description' - className='vsp-process-description' - validations={{maxLength: 1000}} - type='textarea'/> - </Dropzone> - </div> - </ValidationForm> + <div> + {genericFieldInfo && <Form + ref='validationForm' + hasButtons={true} + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + onSubmit={ () => this.submit() } + onReset={ () => onClose() } + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm() } + className='vsp-processes-editor'> + <div className={classnames('vsp-processes-editor-data', {'disabled': isReadOnlyMode})}> + <Dropzone + className={classnames('vsp-process-dropzone-view', {'active-dragging': this.state.dragging})} + onDrop={(acceptedFiles, rejectedFiles) => this.handleImportSubmit(acceptedFiles, rejectedFiles)} + onDragEnter={() => this.setState({dragging: true})} + onDragLeave={() => this.setState({dragging: false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput'> + <GridSection> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name})} + isValid={genericFieldInfo.name.isValid} + isRequired={true} + data-test-id='name' + errorText={genericFieldInfo.name.errorText} + label={i18n('Name')} + value={name} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <FileUploadBox onClick={() => this.refs.processEditorFileInput.open()}/> + </GridItem> + </GridSection> + <GridSection> + <GridItem colSpan={2}> + <Input + name='vsp-process-description' + groupClassName='vsp-process-description' + onChange={description => onDataChanged({description})} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + label={i18n('Notes')} + value={description} + data-test-id='vsp-process-description' + type='textarea'/> + </GridItem> + <GridItem colSpan={2}> + <Input + label={i18n('Artifacts')} + value={artifactName} + type='text' + disabled/> + <Input + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({type: val});} + } + value={type} + label={i18n('Process Type')} + data-test-id='process-type' + isValid={genericFieldInfo.type.isValid} + errorText={genericFieldInfo.type.errorText} + type='select'> + {ProcessesOptionsInputValues.PROCESS_TYPE.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + </GridSection> + </Dropzone> + </div> + </Form>} + </div> ); } @@ -108,14 +166,24 @@ class SoftwareProductProcessesEditorView extends React.Component { } - handleImportSubmit(files) { - let {onDataChanged} = this.props; - this.setState({ - dragging: false, - complete: '0', - files - }); - onDataChanged({artifactName: files[0].name}); + handleImportSubmit(files, rejectedFiles) { + if (files.length > 0) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } + else if (rejectedFiles.length > 0) { + this.setState({ + dragging: false + }); + if (DEBUG) { + console.log('file was rejected.' + rejectedFiles[0].name); + } + } } } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js index 619a2dba0f..20390d1f60 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './SoftwareProductProcessesConstants.js'; export default (state = [], action) => { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx index a2aa3d414e..8f52434042 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Modal from 'nfvo-components/modal/Modal.jsx'; @@ -6,7 +21,7 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import SoftwareProductProcessesEditor from './SoftwareProductProcessesEditor.js'; -import SoftwareProductProcessesConfirmationModal from './SoftwareProductProcessesConfirmationModal.jsx'; + class SoftwareProductProcessesView extends React.Component { @@ -20,30 +35,29 @@ class SoftwareProductProcessesView extends React.Component { onEditProcess: React.PropTypes.func.isRequired, onDeleteProcess: React.PropTypes.func.isRequired, isDisplayEditor: React.PropTypes.bool.isRequired, - isReadOnlyMode: React.PropTypes.bool.isRequired + isReadOnlyMode: React.PropTypes.bool.isRequired, + currentSoftwareProduct:React.PropTypes.object }; render() { - let { currentSoftwareProduct} = this.props; return ( <div className='software-product-landing-view-right-side vsp-processes-page'> {this.renderEditor()} {this.renderProcessList()} - <SoftwareProductProcessesConfirmationModal softwareProductId={currentSoftwareProduct.id}/> </div> ); } renderEditor() { - let {currentSoftwareProduct: {id}, isModalInEditMode, isReadOnlyMode, isDisplayEditor} = this.props; + let {currentSoftwareProduct: {id, version}, isModalInEditMode, isReadOnlyMode, isDisplayEditor} = this.props; return ( - <Modal show={isDisplayEditor} bsSize='large' animation={true}> + <Modal show={isDisplayEditor} bsSize='large' animation={true} className='onborading-modal'> <Modal.Header> <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title> </Modal.Header> <Modal.Body className='edit-process-modal'> - <SoftwareProductProcessesEditor softwareProductId={id} isReadOnlyMode={isReadOnlyMode}/> + <SoftwareProductProcessesEditor softwareProductId={id} version={version} isReadOnlyMode={isReadOnlyMode}/> </Modal.Body> </Modal> ); @@ -60,7 +74,8 @@ class SoftwareProductProcessesView extends React.Component { placeholder={i18n('Filter Process')} onAdd={onAddProcess} isReadOnlyMode={isReadOnlyMode} - onFilter={filter => this.setState({localFilter: filter})}> + title={i18n('Process Details')} + onFilter={value => this.setState({localFilter: value})}> {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} </ListEditorView> ); @@ -68,14 +83,14 @@ class SoftwareProductProcessesView extends React.Component { renderProcessListItem(process, isReadOnlyMode) { let {id, name, description, artifactName = ''} = process; - let {onEditProcess, onDeleteProcess} = this.props; + let {currentSoftwareProduct: {version}, onEditProcess, onDeleteProcess} = this.props; return ( <ListEditorItemView key={id} className='list-editor-item-view' isReadOnlyMode={isReadOnlyMode} onSelect={() => onEditProcess(process)} - onDelete={() => onDeleteProcess(process)}> + onDelete={() => onDeleteProcess(process, version)}> <div className='list-editor-item-view-field'> <div className='title'>{i18n('Name')}</div> diff --git a/openecomp-ui/src/sdc-app/punch-outs.js b/openecomp-ui/src/sdc-app/punch-outs.js index 46e84a60a4..78b64da846 100644 --- a/openecomp-ui/src/sdc-app/punch-outs.js +++ b/openecomp-ui/src/sdc-app/punch-outs.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import '../../resources/scss/onboarding.scss'; import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; diff --git a/openecomp-ui/src/sdc-app/sdc.app.jsx b/openecomp-ui/src/sdc-app/sdc.app.jsx index 0929fa0bb9..5fdea27fea 100644 --- a/openecomp-ui/src/sdc-app/sdc.app.jsx +++ b/openecomp-ui/src/sdc-app/sdc.app.jsx @@ -1,5 +1,19 @@ +/*! + * 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 '../../resources/scss/bootstrap.scss'; -import '../../resources/css/font-awesome.min.css'; import 'react-select/dist/react-select.min.css'; import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; import '../../resources/scss/style.scss'; |