diff options
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups')
9 files changed, 968 insertions, 0 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js new file mode 100644 index 0000000000..c2b269bcf9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; +import FeatureGroupEditorView from './FeatureGroupEditorView.jsx'; + +const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => { + + const {featureGroupEditor} = featureGroup; + + let {data, selectedTab, selectedEntitlementPoolsButtonTab, selectedLicenseKeyGroupsButtonTab} = featureGroupEditor; + + let previousData; + const featureGroupId = data ? data.id : null; + if (featureGroupId) { + previousData = featureGroup.featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId); + } + let {entitlementPoolsList = []} = entitlementPool; + let {licenseKeyGroupsList = []} = licenseKeyGroup; + + return { + data, + previousData, + selectedTab, + selectedEntitlementPoolsButtonTab, + selectedLicenseKeyGroupsButtonTab, + entitlementPoolsList, + licenseKeyGroupsList + }; +}; + + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + 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}); + } + }; +}; + +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 new file mode 100644 index 0000000000..6fecd16b71 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx @@ -0,0 +1,339 @@ +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 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 i18n from 'nfvo-utils/i18n/i18n.js'; + +import {state as FeatureGroupStateConstants} 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) +}); + +class FeatureGroupEditorView extends React.Component { + + + static propTypes = { + data: FeatureGroupsPropType, + previousData: FeatureGroupsPropType, + isReadOnlyMode: React.PropTypes.bool, + + onSubmit: React.PropTypes.func, + onCancel: React.PropTypes.func, + + 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 + }; + + + 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 = { + localEntitlementPoolsListFilter: '', + localLicenseKeyGroupsListFilter: '' + }; + + + render() { + let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + return ( + <ValidationForm + ref='validationForm' + hasButtons={true} + onSubmit={ () => this.submit() } + 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> + ); + } + + submit() { + const {data: featureGroup, previousData: previousFeatureGroup} = this.props; + 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)}); + } + + } +} + + +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 new file mode 100644 index 0000000000..9ea5a31490 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js @@ -0,0 +1,56 @@ +/*- + * ============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 FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; +import FeatureGroupListEditorView from './FeatureGroupListEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => { + const {featureGroupEditor: {data}, featureGroupsList} = featureGroup; + let {vendorName} = licenseModelEditor.data; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + return { + vendorName, + featureGroupsModal: { + show: Boolean(data), + editMode: Boolean(data && data.id) + }, + featureGroupsList, + isReadOnlyMode + }; +}; + + +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, { + featureGroup, + licenseModelId + }) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(FeatureGroupListEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx new file mode 100644 index 0000000000..d998f9216f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -0,0 +1,136 @@ +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 FeatureGroupEditor from './FeatureGroupEditor.js'; +import FeatureGroupsConfirmationModal from './FeatureGroupsConfirmationModal.jsx'; + +class FeatureGroupListEditorView extends React.Component { + static propTypes = { + vendorName: React.PropTypes.string, + licenseModelId: React.PropTypes.string.isRequired, + featureGroupsModal: React.PropTypes.shape({ + show: React.PropTypes.bool, + editMode: React.PropTypes.bool + }), + isReadOnlyMode: React.PropTypes.bool.isRequired, + onAddFeatureGroupClick: React.PropTypes.func, + onEditFeatureGroupClick: React.PropTypes.func, + onDeleteFeatureGroupClick: React.PropTypes.func, + onCancelFeatureGroupsEditor: React.PropTypes.func, + featureGroupsList: React.PropTypes.array + }; + + static defaultProps = { + featureGroupsList: [], + featureGroupsModal: { + show: false, + editMode: false + } + }; + + state = { + localFilter: '' + }; + + render() { + let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick} = this.props; + const {localFilter} = this.state; + + return ( + <div className='feature-groups-list-editor'> + <ListEditorView + title={i18n('Feature Groups for {vendorName} License Model', {vendorName})} + plusButtonTitle={i18n('Add Feature Group')} + filterValue={localFilter} + onFilter={filter => this.setState({localFilter: filter})} + onAdd={() => onAddFeatureGroupClick()} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode))} + </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}/> + + </div> + ); + } + + + renderFeatureGroupListItem(listItem, isReadOnlyMode) { + let {name, description, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem; + return ( + <ListEditorItemView + key={listItem.id} + onDelete={() => this.deleteFeatureGroupItem(listItem)} + onSelect={() => this.editFeatureGroupItem(listItem)} + 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='list-editor-item-view-field'> + <div className='feature-groups-count-field'> + <div className='title'>{i18n('Entitlement')}</div> + <div className='title'>{i18n('Pools')}</div> + <div className='feature-groups-count-ep'>{entitlementPoolsIds.length || 0}</div> + </div> + <div className='feature-groups-count-field'> + <div className='title'>{i18n('License key')}</div> + <div className='title'>{i18n('Groups')}</div> + <div className='feature-groups-count-lk'>{licenseKeyGroupsIds.length || 0}</div> + </div> + </div> + + <div className='list-editor-item-view-field'> + <div className='title'>{i18n('Description')}</div> + <div className='text description'>{description}</div> + </div> + + </ListEditorItemView> + ); + } + + filterList() { + let {featureGroupsList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return featureGroupsList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return featureGroupsList; + } + } + + closeFeatureGroupsEditor() { + this.props.onCancelFeatureGroupsEditor(); + } + + editFeatureGroupItem(featureGroup) { + this.props.onEditFeatureGroupClick(featureGroup); + } + + deleteFeatureGroupItem(featureGroup) { + this.props.onDeleteFeatureGroupClick(featureGroup); + } +} + +export default FeatureGroupListEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js new file mode 100644 index 0000000000..3776c01263 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js @@ -0,0 +1,165 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes as featureGroupsActionConstants} from './FeatureGroupsConstants.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; + +function baseUrl(licenseModelId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/feature-groups`; +} + +function fetchFeatureGroupsList(licenseModelId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +} + +function deleteFeatureGroup(licenseModelId, featureGroupId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${featureGroupId}`); +} + +function addFeatureGroup(licenseModelId, featureGroup) { + return RestAPIUtil.create(baseUrl(licenseModelId), { + name: featureGroup.name, + description: featureGroup.description, + partNumber: featureGroup.partNumber, + addedLicenseKeyGroupsIds: featureGroup.licenseKeyGroupsIds, + addedEntitlementPoolsIds: featureGroup.entitlementPoolsIds + }); +} + +function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup) { + + const {licenseKeyGroupsIds = []} = featureGroup; + const {licenseKeyGroupsIds: prevLicenseKeyGroupsIds = []} = previousFeatureGroup; + const {entitlementPoolsIds = []} = featureGroup; + const {entitlementPoolsIds: prevEntitlementPoolsIds = []} = previousFeatureGroup; + return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${featureGroup.id}`, { + name: featureGroup.name, + description: featureGroup.description, + partNumber: featureGroup.partNumber, + addedLicenseKeyGroupsIds: licenseKeyGroupsIds.filter(licenseKeyGroupId => prevLicenseKeyGroupsIds.indexOf(licenseKeyGroupId) === -1), + removedLicenseKeyGroupsIds: prevLicenseKeyGroupsIds.filter(prevLicenseKeyGroupId => licenseKeyGroupsIds.indexOf(prevLicenseKeyGroupId) === -1), + addedEntitlementPoolsIds: entitlementPoolsIds.filter(entitlementPoolId => prevEntitlementPoolsIds.indexOf(entitlementPoolId) === -1), + removedEntitlementPoolsIds: prevEntitlementPoolsIds.filter(prevEntitlementPoolId => entitlementPoolsIds.indexOf(prevEntitlementPoolId) === -1) + + }); +} + +export default { + fetchFeatureGroupsList(dispatch, {licenseModelId, version}) { + return fetchFeatureGroupsList(licenseModelId, version).then(response => dispatch({ + type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED, + response + })); + }, + + deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId}) { + return deleteFeatureGroup(licenseModelId, featureGroupId).then(() => dispatch({ + type: featureGroupsActionConstants.DELETE_FEATURE_GROUPS, + featureGroupId + })); + }, + + saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}) { + if (previousFeatureGroup) { + return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup).then(() => dispatch({ + type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS, + featureGroup + })); + } + else { + return addFeatureGroup(licenseModelId, featureGroup).then(response => dispatch({ + type: featureGroupsActionConstants.ADD_FEATURE_GROUPS, + featureGroup: { + ...featureGroup, + id: response.value + } + })); + } + }, + + selectEntitlementPoolsEditorTab(dispatch, {tab}) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.SELECT_TAB, + tab + }); + }, + + 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}); + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.OPEN, + featureGroup + }); + }, + + closeFeatureGroupsEditor(dispatch) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.CLOSE + }); + }, + + 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(() => { + this.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx new file mode 100644 index 0000000000..142ec3c4c8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000000..e02c54595d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js @@ -0,0 +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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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({ + FEATURE_GROUPS_LIST_LOADED: null, + ADD_FEATURE_GROUPS: null, + EDIT_FEATURE_GROUPS: null, + DELETE_FEATURE_GROUPS: null, + FEATURE_GROUPS_DELETE_CONFIRM: null, + + + ENTITLEMENT_POOLS_LIST_LOADED: null, + + featureGroupsEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + SELECT_TAB: null, + SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: null, + SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: null + } +}); + +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 + }, +}); + + + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js new file mode 100644 index 0000000000..576a5358e6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js @@ -0,0 +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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 './FeatureGroupsConstants.js'; + + + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.featureGroupsEditor.OPEN: + return { + ...state, + data: action.featureGroup || {} + }; + case actionTypes.featureGroupsEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.featureGroupsEditor.CLOSE: + return {}; + case actionTypes.featureGroupsEditor.SELECT_TAB: + return { + ...state, + selectedTab: action.tab + }; + + case actionTypes.featureGroupsEditor.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: + return { + ...state, + selectedEntitlementPoolsButtonTab: action.buttonTab + }; + case actionTypes.featureGroupsEditor.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: + return { + ...state, + selectedLicenseKeyGroupsButtonTab: action.buttonTab + }; + default: + return state; + } + +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js new file mode 100644 index 0000000000..5cf3248919 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js @@ -0,0 +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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 './FeatureGroupsConstants.js'; +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FEATURE_GROUPS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.ADD_FEATURE_GROUPS: + return [...state, action.featureGroup]; + case actionTypes.EDIT_FEATURE_GROUPS: + const indexForEdit = state.findIndex(featureGroup => featureGroup.id === action.featureGroup.id); + return [...state.slice(0, indexForEdit), action.featureGroup, ...state.slice(indexForEdit + 1)]; + case actionTypes.DELETE_FEATURE_GROUPS: + return state.filter(featureGroup => featureGroup.id !== action.featureGroupId); + default: + return state; + } +}; |