summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups')
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js66
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx339
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js56
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx136
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js165
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx48
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js60
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js62
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js36
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;
+ }
+};