diff options
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/licenseModel')
47 files changed, 1472 insertions, 421 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js index 4ba10c3a68..960090c2d8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js @@ -14,6 +14,7 @@ * 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({ LICENSE_MODEL_LOADED: null, @@ -31,3 +32,16 @@ export const navigationItems = keyMirror({ LICENSE_KEY_GROUPS: 'license-key-groups', ACTIVITY_LOG: 'activity-log' }); + +export const thresholdUnitType = { + ABSOLUTE: 'Absolute', + PERCENTAGE: 'Percentage' +}; + +export const optionsInputValues = { + THRESHOLD_UNITS: [ + {enum: '', title: i18n('please select…')}, + {enum: thresholdUnitType.ABSOLUTE, title: 'Absolute'}, + {enum: thresholdUnitType.PERCENTAGE, title: '%'} + ] +};
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js index bd060a4c28..80beda22b5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js @@ -35,6 +35,7 @@ import licenseKeyGroupsListReducer from './licenseKeyGroups/LicenseKeyGroupsList import {createPlainDataReducer} from 'sdc-app/common/reducers/PlainDataReducer.js'; import {actionTypes as licenseModelOverviewConstants, selectedButton, VLM_DESCRIPTION_FORM} from './overview/LicenseModelOverviewConstants.js'; +import limitEditorReducer from './limits/LimitEditorReducer.js'; export default combineReducers({ licenseModelCreation: createPlainDataReducer(licenseModelCreationReducer), @@ -81,5 +82,6 @@ export default combineReducers({ } } )}), + limitEditor: createPlainDataReducer(limitEditorReducer), activityLog: activityLogReducer }); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelValidations.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelValidations.js new file mode 100644 index 0000000000..64bae3b6a0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelValidations.js @@ -0,0 +1,41 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 i18n from 'nfvo-utils/i18n/i18n.js'; +import {thresholdUnitType} from './LicenseModelConstants.js'; +import Validator from 'nfvo-utils/Validator.js'; + +export function validateStartDate(value, state) { + if (state.data.expiryDate) { + if (!value) { + return {isValid: false, errorText: i18n('Start date has to be specified if expiry date is specified')}; + } + } + return {isValid: true, errorText: ''}; +} + +export function thresholdValueValidation(value, state) { + let unit = state.data.thresholdUnits; + if (unit === thresholdUnitType.PERCENTAGE) { + return Validator.validate('thresholdValue', value, [ + {type: 'numeric', data: true}, + {type: 'maximum', data: 100}, + {type: 'minimum', data: 0}]); + } else { + return Validator.validate('thresholdValue', value, [ + {type: 'numeric', data: true}, + ]); + } +} 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 d85618c85f..39b3c15c6a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js @@ -26,7 +26,7 @@ export const mapStateToProps = ({licenseModelList, licenseModel: {licenseModelCr let VLMNames = {}; for (let i = 0; i < licenseModelList.length; i++) { - VLMNames[licenseModelList[i].vendorName] = licenseModelList[i].id; + VLMNames[licenseModelList[i].vendorName.toLowerCase()] = licenseModelList[i].id; } return {...licenseModelCreation, isFormValid: isFormValid, VLMNames}; 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 a7c95f608d..a371d5635e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js @@ -17,6 +17,8 @@ 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'; +import {actionTypes as limitEditorActions} from 'sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js'; +import getValue from 'nfvo-utils/getValue.js'; function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); @@ -28,18 +30,16 @@ function fetchEntitlementPoolsList(licenseModelId, version) { return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } -function postEntitlementPool(licenseModelId, entitlementPool, version) { +function postEntitlementPool(licenseModelId, entitlementPool, version) { return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, - thresholdUnits: entitlementPool.thresholdUnits, + thresholdUnits: getValue(entitlementPool.thresholdUnits), entitlementMetric: entitlementPool.entitlementMetric, increments: entitlementPool.increments, - aggregationFunction: entitlementPool.aggregationFunction, - operationalScope: entitlementPool.operationalScope, + operationalScope: getValue(entitlementPool.operationalScope), time: entitlementPool.time, - manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber, startDate: entitlementPool.startDate, expiryDate: entitlementPool.expiryDate }); @@ -47,17 +47,16 @@ function postEntitlementPool(licenseModelId, entitlementPool, version) { function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool, version) { + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${entitlementPool.id}`, { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, - thresholdUnits: entitlementPool.thresholdUnits, + thresholdUnits: getValue(entitlementPool.thresholdUnits), entitlementMetric: entitlementPool.entitlementMetric, increments: entitlementPool.increments, - aggregationFunction: entitlementPool.aggregationFunction, - operationalScope: entitlementPool.operationalScope, + operationalScope: getValue(entitlementPool.operationalScope), time: entitlementPool.time, - manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber, startDate: entitlementPool.startDate, expiryDate: entitlementPool.expiryDate }); @@ -67,8 +66,43 @@ function deleteEntitlementPool(licenseModelId, entitlementPoolId, version) { return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}`); } +function fetchLimitsList(licenseModelId, entitlementPoolId, version) { + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}/limits`); +} + +function deleteLimit(licenseModelId, entitlementPoolId, version, limitId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}/limits/${limitId}`); +} + +function postLimit(licenseModelId, entitlementPoolId, version, limit) { + return RestAPIUtil.post(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}/limits`, { + name: limit.name, + type: limit.type, + description: limit.description, + metric: limit.metric, + value: limit.value, + unit: limit.unit, + aggregationFunction: getValue(limit.aggregationFunction), + time: getValue(limit.time) + }); +} + +function putLimit(licenseModelId, entitlementPoolId, version, limit) { + + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${entitlementPoolId}/limits/${limit.id}`, { + name: limit.name, + type: limit.type, + description: limit.description, + metric: limit.metric, + value: limit.value, + unit: limit.unit, + aggregationFunction: getValue(limit.aggregationFunction), + time: getValue(limit.time) + }); +} export default { + fetchEntitlementPoolsList(dispatch, {licenseModelId, version}) { return fetchEntitlementPoolsList(licenseModelId, version).then(response => dispatch({ type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_LIST_LOADED, @@ -76,7 +110,10 @@ export default { })); }, - openEntitlementPoolsEditor(dispatch, {entitlementPool} = {}) { + openEntitlementPoolsEditor(dispatch, {entitlementPool, licenseModelId, version} = {}) { + if (licenseModelId && version) { + this.fetchLimits(dispatch, {licenseModelId, version, entitlementPool}); + } dispatch({ type: entitlementPoolsActionTypes.entitlementPoolsEditor.OPEN, entitlementPool @@ -145,5 +182,32 @@ export default { LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { this.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); }); + }, + + + fetchLimits(dispatch, {licenseModelId, version, entitlementPool}) { + return fetchLimitsList(licenseModelId, entitlementPool.id, version). then (response => { + dispatch({ + type: entitlementPoolsActionTypes.entitlementPoolsEditor.LIMITS_LIST_LOADED, + response + }); + }); + }, + + submitLimit(dispatch, {licenseModelId, version, entitlementPool, limit}) { + const propmise = limit.id ? putLimit(licenseModelId,entitlementPool.id, version, limit) + : postLimit(licenseModelId,entitlementPool.id, version, limit); + return propmise.then(() => { + dispatch({ + type: limitEditorActions.CLOSE + }); + this.fetchLimits(dispatch, {licenseModelId, version, entitlementPool}); + }); + }, + + deleteLimit(dispatch, {licenseModelId, version, entitlementPool, limit}) { + return deleteLimit(licenseModelId,entitlementPool.id, version, limit.id).then(() => { + this.fetchLimits(dispatch, {licenseModelId, version, entitlementPool}); + }); } }; 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 761614dfeb..de2a87ceaf 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js @@ -15,7 +15,7 @@ */ 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'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; export const actionTypes = keyMirror({ @@ -28,6 +28,7 @@ export const actionTypes = keyMirror({ OPEN: null, CLOSE: null, DATA_CHANGED: null, + LIMITS_LIST_LOADED: null } }); @@ -94,11 +95,6 @@ export const optionsInputValues = { {enum: 'Units_TB', title: 'Units-TB'}, {enum: 'Units_GB', title: 'Units-GB'}, {enum: 'Units_MB', title: 'Units-MB'} - ], - THRESHOLD_UNITS: [ - {enum: '', title: i18n('please select…')}, - {enum: thresholdUnitType.ABSOLUTE, title: 'Absolute'}, - {enum: thresholdUnitType.PERCENTAGE, title: '%'} ] }; @@ -112,6 +108,11 @@ export const extractUnits = (units) => { return units === 'Absolute' ? '' : '%'; }; -export const SP_ENTITLEMENT_POOL_FORM = 'SPENTITLEMENTPOOL'; +export const tabIds = { + GENERAL: 'GENERAL', + SP_LIMITS: 'SP_LIMITS', + VENDOR_LIMITS: 'VENDOR_LIMITS', + ADD_LIMIT_BUTTON: 'ADD_LIMIT_BUTTON' +}; -export const EP_TIME_FORMAT = 'MM/DD/YYYY'; +export const SP_ENTITLEMENT_POOL_FORM = 'SPENTITLEMENTPOOL';
\ No newline at end of file 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 f89cf8fbb5..23c260f793 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js @@ -18,10 +18,12 @@ import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; import EntitlementPoolsEditorView from './EntitlementPoolsEditorView.jsx'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import LimitEditorActionHelper from '../limits/LimitEditorActionHelper.js'; + const mapStateToProps = ({licenseModel: {entitlementPool}}) => { - let {data, genericFieldInfo, formReady} = entitlementPool.entitlementPoolEditor; + let {data, genericFieldInfo, formReady, limitsList} = entitlementPool.entitlementPoolEditor; let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); @@ -33,7 +35,7 @@ const mapStateToProps = ({licenseModel: {entitlementPool}}) => { const list = entitlementPool.entitlementPoolsList; for (let i = 0; i < list.length; i++) { - EPNames[list[i].name] = list[i].id; + EPNames[list[i].name.toLowerCase()] = list[i].id; } return { @@ -42,7 +44,8 @@ const mapStateToProps = ({licenseModel: {entitlementPool}}) => { previousData, isFormValid, formReady, - EPNames + EPNames, + limitsList }; }; @@ -50,11 +53,13 @@ const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch), - onSubmit: ({previousEntitlementPool, entitlementPool}) => { - EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch); + onSubmit: ({previousEntitlementPool, entitlementPool, keepOpen}) => { + if (!keepOpen) {EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch);} EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool, version}); }, - onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName), + onCloseLimitEditor: () => LimitEditorActionHelper.closeLimitsEditor(dispatch), + onOpenLimitEditor: (limit) => LimitEditorActionHelper.openLimitsEditor(dispatch, {limit}) }; }; 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 bc9549765f..be100f6802 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js @@ -41,12 +41,7 @@ export default (state = {}, action) => { '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}] + validations: [{type: 'maxLength', data: 1000}] }, 'increments' : { isValid: true, @@ -56,33 +51,18 @@ export default (state = {}, action) => { 'operationalScope' : { isValid: true, errorText: '', - validations: [{type: 'required', data: true}] + validations: [] }, 'thresholdUnits' : { isValid: true, errorText: '', - validations: [{type: 'required', data: true}] + validations: [] }, '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}] - }, + validations: [] + }, 'startDate': { isValid: true, errorText: '', @@ -106,6 +86,12 @@ export default (state = {}, action) => { }; case actionTypes.entitlementPoolsEditor.CLOSE: return {}; + + case actionTypes.entitlementPoolsEditor.LIMITS_LIST_LOADED: + return { + ...state, + limitsList: action.response.results + }; default: return state; } 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 e4b52fc439..aa1321c24a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx @@ -21,41 +21,39 @@ import Validator from 'nfvo-utils/Validator.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 Button from 'sdc-ui/lib/react/Button.js'; 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, EP_TIME_FORMAT} from './EntitlementPoolsConstants.js'; -import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import {optionsInputValues as EntitlementPoolsOptionsInputValues, SP_ENTITLEMENT_POOL_FORM, tabIds} from './EntitlementPoolsConstants.js'; +import {optionsInputValues as LicenseModelOptionsInputValues} from '../LicenseModelConstants.js'; +import {validateStartDate, thresholdValueValidation} from '../LicenseModelValidations.js'; +import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js'; +import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; +import Tabs from 'sdc-ui/lib/react/Tabs.js'; +import Tab from 'sdc-ui/lib/react/Tab.js'; +import EntitlementPoolsLimits from './EntitlementPoolsLimits.js'; +import {limitType, NEW_LIMIT_TEMP_ID} from '../limits/LimitEditorConstants.js'; const EntitlementPoolPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, - manufacturerReferenceNumber: React.PropTypes.string, operationalScope: React.PropTypes.shape({ choices: React.PropTypes.array, other: React.PropTypes.string }), - aggregationFunction: React.PropTypes.shape({ - choice: React.PropTypes.string, - other: React.PropTypes.string - }), + thresholdUnits: React.PropTypes.string, + thresholdValue: React.PropTypes.number, increments: React.PropTypes.string, - time: React.PropTypes.shape({ - choice: React.PropTypes.string, - other: React.PropTypes.string - }), - entitlementMetric: React.PropTypes.shape({ - choice: React.PropTypes.string, - other: React.PropTypes.string - }) + startDate: React.PropTypes.string, + expiryDate: React.PropTypes.string }); -const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, validateChoiceWithOther, validateTimeOtherValue, +const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, validateName, thresholdValueValidation, validateStartDate}) => { - let { - name, description, manufacturerReferenceNumber, operationalScope , aggregationFunction, thresholdUnits, thresholdValue, - increments, time, entitlementMetric, startDate, expiryDate} = data; + let {name, description, operationalScope, thresholdUnits, thresholdValue, + increments, startDate, expiryDate} = data; return ( <GridSection> <GridItem colSpan={2}> @@ -73,12 +71,10 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val <InputOptions onInputChange={()=>{}} isMultiSelect={true} - - isRequired={true} onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}}, - SP_ENTITLEMENT_POOL_FORM, {operationalScope: validateChoiceWithOther})} + SP_ENTITLEMENT_POOL_FORM)} onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], - other: operationalScope}}, SP_ENTITLEMENT_POOL_FORM, {operationalScope: validateChoiceWithOther})} + other: operationalScope}}, SP_ENTITLEMENT_POOL_FORM)} label={i18n('Operational Scope')} data-test-id='create-ep-operational-scope' type='select' @@ -95,21 +91,21 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val 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});} + if(thresholdValue && thresholdValue !== '') { + onDataChanged({thresholdValue: thresholdValue}, SP_ENTITLEMENT_POOL_FORM,{thresholdValue : thresholdValueValidation}); + }} } value={thresholdUnits} @@ -120,7 +116,7 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val groupClassName='bootstrap-input-options' className='input-options-select' type='select' > - {EntitlementPoolsOptionsInputValues.THRESHOLD_UNITS.map(mtype => + {LicenseModelOptionsInputValues.THRESHOLD_UNITS.map(mtype => <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} </Input> @@ -133,116 +129,46 @@ const EntitlementPoolsFormContent = ({data, genericFieldInfo, onDataChanged, val 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} - isValid={genericFieldInfo.manufacturerReferenceNumber.isValid} - errorText={genericFieldInfo.manufacturerReferenceNumber.errorText} - 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> - <GridItem colSpan={2} /> - <GridItem colSpan={2}> - <Input - type='date' - label={i18n('Start Date')} - value={startDate} - dateFormat={EP_TIME_FORMAT} - startDate={startDate} - endDate={expiryDate} - onChange={startDate => onDataChanged( - {startDate: startDate ? startDate.format(EP_TIME_FORMAT) : ''}, - SP_ENTITLEMENT_POOL_FORM, - {startDate: validateStartDate} - )} - isValid={genericFieldInfo.startDate.isValid} - errorText={genericFieldInfo.startDate.errorText} - selectsStart/> - </GridItem> - <GridItem colSpan={2}> - <Input - type='date' - label={i18n('Expiry Date')} - value={expiryDate} - dateFormat={EP_TIME_FORMAT} - startDate={startDate} - endDate={expiryDate} - onChange={expiryDate => { - onDataChanged({expiryDate: expiryDate ? expiryDate.format(EP_TIME_FORMAT) : ''}, SP_ENTITLEMENT_POOL_FORM); - onDataChanged({startDate}, SP_ENTITLEMENT_POOL_FORM, {startDate: validateStartDate}); - }} - isValid={genericFieldInfo.expiryDate.isValid} - errorText={genericFieldInfo.expiryDate.errorText} - selectsEnd/> - </GridItem> + <div className='date-section'> + <Input + type='date' + label={i18n('Start Date')} + value={startDate} + dateFormat={DATE_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={startDate => onDataChanged( + {startDate: startDate ? startDate.format(DATE_FORMAT) : ''}, + SP_ENTITLEMENT_POOL_FORM, + {startDate: validateStartDate} + )} + isValid={genericFieldInfo.startDate.isValid} + errorText={genericFieldInfo.startDate.errorText} + selectsStart/> + <Input + type='date' + label={i18n('Expiry Date')} + value={expiryDate} + dateFormat={DATE_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={expiryDate => { + onDataChanged({expiryDate: expiryDate ? expiryDate.format(DATE_FORMAT) : ''}, SP_ENTITLEMENT_POOL_FORM); + onDataChanged({startDate}, SP_ENTITLEMENT_POOL_FORM, {startDate: validateStartDate}); + }} + isValid={genericFieldInfo.expiryDate.isValid} + errorText={genericFieldInfo.expiryDate.errorText} + selectsEnd/> + </div> + </GridItem> </GridSection> ); }; @@ -263,42 +189,102 @@ class EntitlementPoolsEditorView extends React.Component { data: {} }; - render() { - let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo} = this.props; + componentDidUpdate(prevProps) { + if (this.props.formReady && this.props.formReady !== prevProps.formReady) { // if form validation succeeded -> continue with submit + this.submit(); + } + } + + state = { + selectedTab: tabIds.GENERAL, + selectedLimit: '' + }; + render() { + let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo, onCloseLimitEditor, limitsList = []} = this.props; + const {selectedTab} = this.state; + const isTabsDisabled = !data.id || !this.props.isFormValid; return ( <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)} - validateStartDate={(value, state)=> this.validateStartDate(value, state)} - thresholdValueValidation={(value, state)=> this.thresholdValueValidation(value, state)}/> - </Form> + <Tabs + type='menu' + activeTab={selectedTab} + onTabClick={(tabIndex)=>{ + if (tabIndex === tabIds.ADD_LIMIT_BUTTON) { + this.onAddLimit(); + } else { + this.setState({selectedTab: tabIndex}); + this.setState({selectedLimit: ''}); + onCloseLimitEditor(); + } + + }} + invalidTabs={[]}> + <Tab tabId={tabIds.GENERAL} data-test-id='general-tab' title={i18n('General')}> + { + genericFieldInfo && <Form + ref='validationForm' + hasButtons={false} + labledButtons={false} + isReadOnlyMode={isReadOnlyMode} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(SP_ENTITLEMENT_POOL_FORM) } + className='license-model-form entitlement-pools-form'> + <EntitlementPoolsFormContent + data={data} + genericFieldInfo={genericFieldInfo} + onDataChanged={onDataChanged} + validateName={(value) => this.validateName(value)} + validateStartDate={(value, state) => validateStartDate(value, state)} + thresholdValueValidation={(value, state) => thresholdValueValidation(value, state)}/> + </Form> + } + </Tab> + <Tab disabled={isTabsDisabled} tabId={tabIds.SP_LIMITS} data-test-id='sp-limits-tab' title={i18n('SP Limits')}> + {selectedTab === tabIds.SP_LIMITS && + <EntitlementPoolsLimits + limitType={limitType.SERVICE_PROVIDER} + limitsList={limitsList.filter(item => item.type === limitType.SERVICE_PROVIDER)} + selectedLimit={this.state.selectedLimit} + onCloseLimitEditor={() => this.onCloseLimitEditor()} + onSelectLimit={limit => this.onSelectLimit(limit)}/>} + </Tab> + <Tab disabled={isTabsDisabled} tabId={tabIds.VENDOR_LIMITS} data-test-id='vendor-limits-tab' title={i18n('Vendor Limits')}> + {selectedTab === tabIds.VENDOR_LIMITS && + <EntitlementPoolsLimits + limitType={limitType.VENDOR} + limitsList={limitsList.filter(item => item.type === limitType.VENDOR)} + selectedLimit={this.state.selectedLimit} + onCloseLimitEditor={() => this.onCloseLimitEditor()} + onSelectLimit={limit => this.onSelectLimit(limit)}/>} + </Tab> + {selectedTab !== tabIds.GENERAL ? + <Button disabled={this.state.selectedLimit} className='add-limit-button' tabId={tabIds.ADD_LIMIT_BUTTON} btnType='link' iconName='plus'>{i18n('Add Limit')}</Button> : + <div></div> // Render empty div to not break tabs } + </Tabs> + <GridSection className='license-model-modal-buttons entitlement-pools-editor-buttons'> + {!this.state.selectedLimit && <Button btnType='default' disabled={!this.props.isFormValid} onClick={() => this.submit()} type='reset'>{i18n('Save')}</Button>} + <Button btnType={this.state.selectedLimit ? 'default' : 'outline'} onClick={() => this.props.onCancel()} type='reset'> + {i18n('Cancel')} + </Button> + </GridSection> </div> ); } submit() { - const {data: entitlementPool, previousData: previousEntitlementPool} = this.props; - this.props.onSubmit({entitlementPool, previousEntitlementPool}); + const {data: entitlementPool, previousData: previousEntitlementPool, formReady} = this.props; + + if (!formReady) { + this.props.onValidateForm(SP_ENTITLEMENT_POOL_FORM); + } else { + this.props.onSubmit({entitlementPool, previousEntitlementPool}); + } + + } validateName(value) { @@ -309,52 +295,26 @@ class EntitlementPoolsEditorView extends React.Component { {isValid: false, errorText: i18n('Entitlement pool by the name \'' + value + '\' already exists. Entitlement pool name must be unique')}; } - validateStartDate(value, state) { - if (state.data.expiryDate) { - if (!value) { - return {isValid: false, errorText: i18n('Start date has to be specified if expiry date is specified')}; - } + onSelectLimit(limit) { + if (limit.id === this.state.selectedLimit) { + this.setState({selectedLimit: ''}); + return; } - return {isValid: true, errorText: ''}; + this.setState({selectedLimit: limit.id}); + this.props.onOpenLimitEditor(limit); } - validateTimeOtherValue(value) { - return Validator.validate('time', value.other, [{type: 'required', data: true}, {type: 'numeric', data: true}]); + onCloseLimitEditor() { + this.setState({selectedLimit: ''}); + this.props.onCloseLimitEditor(); } - 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}]); - } + onAddLimit() { + this.setState({selectedLimit: NEW_LIMIT_TEMP_ID}); + this.props.onOpenLimitEditor(); } - 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}]); - } - } } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsLimits.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsLimits.js new file mode 100644 index 0000000000..ae53a753c0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsLimits.js @@ -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 {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import Limits from 'sdc-app/onboarding/licenseModel/limits/Limits.jsx'; +import {actionTypes as globalModalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; + +const mapStateToProps = ({licenseModel: {entitlementPool: {entitlementPoolEditor: {data}}, limitEditor}, currentScreen}) => { + let {props: {licenseModelId, version}} = currentScreen; + return { + parent: data, + limitEditor, + licenseModelId, + version + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), + onSubmit: (limit, entitlementPool, licenseModelId, version) => EntitlementPoolsActionHelper.submitLimit(dispatch, + { + limit, + entitlementPool, + licenseModelId, + version}), + onDelete: ({limit, parent, licenseModelId, version, onCloseLimitEditor, selectedLimit}) => dispatch({ + type: globalModalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete ${limit.name}?`), + confirmationButtonText: i18n('Delete'), + title: i18n('Warning'), + onConfirmed: ()=> EntitlementPoolsActionHelper.deleteLimit(dispatch, {limit, entitlementPool: parent, licenseModelId, version}).then(() => + selectedLimit === limit.id && onCloseLimitEditor() + ) + } + }) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(Limits);
\ No newline at end of file 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 993ed48f2b..62c6663833 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js @@ -39,7 +39,7 @@ const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onAddEntitlementPoolClick: () => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch), - onEditEntitlementPoolClick: entitlementPool => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch, {entitlementPool}), + onEditEntitlementPoolClick: entitlementPool => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch, {entitlementPool, licenseModelId, version}), onDeleteEntitlementPool: entitlementPool => dispatch({ type: globalMoadlActions.GLOBAL_MODAL_WARNING, data:{ 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 55fd11b8bb..c730d669c7 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx @@ -21,7 +21,7 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import EntitlementPoolsEditor from './EntitlementPoolsEditor.js'; -import {extractUnits, extractValue} from './EntitlementPoolsConstants'; +import {extractUnits} from './EntitlementPoolsConstants'; class EntitlementPoolsListEditorView extends React.Component { static propTypes = { @@ -60,7 +60,7 @@ class EntitlementPoolsListEditorView extends React.Component { isReadOnlyMode={isReadOnlyMode}> {this.filterList().map(entitlementPool => this.renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode))} </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal entitlement-pools-modal'> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal license-model-modal entitlement-pools-modal'> <Modal.Header> <Modal.Title>{`${isModalInEditMode ? i18n('Edit Entitlement Pool') : i18n('Create New Entitlement Pool')}`}</Modal.Title> </Modal.Header> @@ -91,8 +91,7 @@ class EntitlementPoolsListEditorView extends React.Component { } renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode) { - let {id, name, description, thresholdValue, thresholdUnits, entitlementMetric, aggregationFunction, - manufacturerReferenceNumber, time} = entitlementPool; + let {id, name, description, thresholdValue, thresholdUnits} = entitlementPool; let {onEditEntitlementPoolClick, onDeleteEntitlementPool} = this.props; return ( <ListEditorItemView @@ -109,13 +108,7 @@ class EntitlementPoolsListEditorView extends React.Component { <div className='list-editor-item-view-field'> <div className='title'>{i18n('Entitlement')}</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'> - <div className='title'>{i18n('Manufacturer Reference Number')}</div> - <div className='text contract-number'>{manufacturerReferenceNumber}</div> + <div className='entitlement-pools-count'>{thresholdValue && `${thresholdValue} ${extractUnits(thresholdUnits)}`}</div> </div> <div className='list-editor-item-view-field'> 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 c6249c98ca..6edb1e1f0c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js @@ -34,7 +34,7 @@ export const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, l } for (let i = 0; i < list.length; i++) { - FGNames[list[i].name] = list[i].id; + FGNames[list[i].name.toLowerCase()] = list[i].id; } for (let field in genericFieldInfo) { 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 d69548442c..6a5ee4617c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx @@ -15,7 +15,7 @@ */ import React from 'react'; import Tabs from 'nfvo-components/input/validation/Tabs.jsx'; -import Tab from 'react-bootstrap/lib/Tab.js'; +import Tab from 'sdc-ui/lib/react/Tab.js'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; import {TabsForm as Form} from 'nfvo-components/input/validation/Form.jsx'; @@ -31,12 +31,13 @@ const FeatureGroupsPropType = React.PropTypes.shape({ name: React.PropTypes.string, description: React.PropTypes.string, partNumber: React.PropTypes.string, + manufacturerReferenceNumber: 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; + let {name, description, partNumber, manufacturerReferenceNumber} = data; return ( <GridSection> <GridItem colSpan={2}> @@ -51,6 +52,8 @@ const GeneralTab = ({data = {}, onDataChanged, genericFieldInfo, validateName}) isRequired={true} isValid={genericFieldInfo.name.isValid} errorText={genericFieldInfo.name.errorText} /> + </GridItem> + <GridItem colSpan={2}> <Input groupClassName='field-section' className='description-field' @@ -60,9 +63,10 @@ const GeneralTab = ({data = {}, onDataChanged, genericFieldInfo, validateName}) value={description} name='feature-group-description' type='textarea' - isRequired={true} isValid={genericFieldInfo.description.isValid} errorText={genericFieldInfo.description.errorText} /> + </GridItem> + <GridItem colSpan={2}> <Input groupClassName='field-section' onChange={partNumber => onDataChanged({partNumber}, FG_EDITOR_FORM)} @@ -74,6 +78,18 @@ const GeneralTab = ({data = {}, onDataChanged, genericFieldInfo, validateName}) isValid={genericFieldInfo.partNumber.isValid} errorText={genericFieldInfo.partNumber.errorText} /> </GridItem> + <GridItem colSpan={2}> + <Input + groupClassName='field-section' + onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber}, FG_EDITOR_FORM)} + label={i18n('Manufacturer Reference Number')} + data-test-id='create-fg-reference-number' + value={manufacturerReferenceNumber} + isRequired={true} + type='text' + isValid={genericFieldInfo.manufacturerReferenceNumber.isValid} + errorText={genericFieldInfo.manufacturerReferenceNumber.errorText} /> + </GridItem> </GridSection> ); }; @@ -94,7 +110,7 @@ const EntitlementPoolsTab = ({entitlementPoolsList, data, onDataChanged, isReadO ); } else { return ( - <p>{i18n('There is no available entitlement pools')}</p> + <p>{i18n('There are no available entitlement pools')}</p> ); } }; @@ -115,7 +131,7 @@ const LKGTab = ({licenseKeyGroupsList, data, onDataChanged, isReadOnlyMode}) => ); } else { return ( - <p>{i18n('There is no available licsense key groups')}</p> + <p>{i18n('There are no available license key groups')}</p> ); } }; @@ -166,22 +182,22 @@ class FeatureGroupEditorView extends React.Component { labledButtons={true} isReadOnlyMode={isReadOnlyMode} name='feature-group-validation-form' - className='feature-group-form'> - <Tabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect} invalidTabs={invalidTabs} id='vlmFGValTabs' > - <Tab eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL} title={i18n('General')} > + className='license-model-form feature-group-form'> + <Tabs activeTab={onTabSelect ? selectedTab : undefined} onTabClick={onTabSelect} invalidTabs={invalidTabs} id='vlmFGValTabs' > + <Tab tabId={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL} title={i18n('General')} > <fieldset disabled={isReadOnlyMode}> <GeneralTab data={data} onDataChanged={onDataChanged} genericFieldInfo={genericFieldInfo} validateName={(value)=> this.validateName(value)}/> </fieldset> </Tab> <Tab - eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS} + tabId={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS} title={i18n('Entitlement Pools')} > <fieldset disabled={isReadOnlyMode}> <EntitlementPoolsTab isReadOnlyMode={isReadOnlyMode} data={data} onDataChanged={onDataChanged} entitlementPoolsList={entitlementPoolsList} /> </fieldset> </Tab> <Tab - eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENSE_KEY_GROUPS} + tabId={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENSE_KEY_GROUPS} title={i18n('License Key Groups')} > <fieldset disabled={isReadOnlyMode}> <LKGTab isReadOnlyMode={isReadOnlyMode} data={data} onDataChanged={onDataChanged} licenseKeyGroupsList={licenseKeyGroupsList} /> 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 f883bd7a14..9844290a25 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -65,7 +65,7 @@ class FeatureGroupListEditorView extends React.Component { {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode, version))} </ListEditorView> {featureGroupsModal.show && <Modal show={featureGroupsModal.show} bsSize='large' animation={true} - className='onborading-modal feature-group-modal'> + className='onborading-modal license-model-modal feature-group-modal'> <Modal.Header> <Modal.Title>{`${featureGroupsModal.editMode ? i18n('Edit Feature Group') : i18n('Create New Feature Group')}`}</Modal.Title> </Modal.Header> @@ -84,7 +84,7 @@ class FeatureGroupListEditorView extends React.Component { renderFeatureGroupListItem(listItem, isReadOnlyMode, version) { - let {name, description, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem; + let {name, description, manufacturerReferenceNumber, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem; return ( <ListEditorItemView key={listItem.id} @@ -111,9 +111,19 @@ class FeatureGroupListEditorView extends React.Component { </div> <div className='list-editor-item-view-field'> + <div className='feature-groups-count-field'> + <div className='title'>{i18n('Manufacturer Reference')}</div> + <div className='title'>{i18n('Number')}</div> + <div className='feature-groups-count-ep'>{manufacturerReferenceNumber}</div> + </div> + </div> + + <div className='list-editor-item-view-field'> <div className='title'>{i18n('Description')}</div> <div className='text description'>{description}</div> </div> + + </ListEditorItemView> ); 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 a2015787a6..b8c03750fb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js @@ -39,6 +39,7 @@ function addFeatureGroup(licenseModelId, featureGroup, version) { name: featureGroup.name, description: featureGroup.description, partNumber: featureGroup.partNumber, + manufacturerReferenceNumber: featureGroup.manufacturerReferenceNumber, addedLicenseKeyGroupsIds: featureGroup.licenseKeyGroupsIds, addedEntitlementPoolsIds: featureGroup.entitlementPoolsIds }); @@ -54,6 +55,7 @@ function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup, name: featureGroup.name, description: featureGroup.description, partNumber: featureGroup.partNumber, + manufacturerReferenceNumber: featureGroup.manufacturerReferenceNumber, addedLicenseKeyGroupsIds: licenseKeyGroupsIds.filter(licenseKeyGroupId => prevLicenseKeyGroupsIds.indexOf(licenseKeyGroupId) === -1), removedLicenseKeyGroupsIds: prevLicenseKeyGroupsIds.filter(prevLicenseKeyGroupId => licenseKeyGroupsIds.indexOf(prevLicenseKeyGroupId) === -1), addedEntitlementPoolsIds: entitlementPoolsIds.filter(entitlementPoolId => prevEntitlementPoolsIds.indexOf(entitlementPoolId) === -1), 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 001bd20d44..5688fc0f16 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js @@ -30,7 +30,7 @@ export default (state = {}, action) => { 'description': { isValid: true, errorText: '', - validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}], + validations: [{type: 'maxLength', data: 1000}], tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL }, 'partNumber': { @@ -39,6 +39,12 @@ export default (state = {}, action) => { validations: [{type: 'required', data: true}], tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL }, + 'manufacturerReferenceNumber': { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 100}], + tabId: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL + }, 'name': { isValid: true, errorText: '', 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 998d5f0e8d..40bef2c181 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js @@ -15,7 +15,7 @@ */ 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'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; export const actionTypes = keyMirror({ LICENSE_AGREEMENT_LIST_LOADED: null, 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 aada8ddca1..7d70da6ea5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js @@ -32,7 +32,7 @@ export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}} } for (let i = 0; i < list.length; i++) { - LANames[list[i].name] = list[i].id; + LANames[list[i].name.toLowerCase()] = list[i].id; } const {featureGroupsList = []} = featureGroup; 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 e02935c579..5be140550a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js @@ -26,7 +26,7 @@ export default (state = {}, action) => { 'description' : { isValid: true, errorText: '', - validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}], + validations: [{type: 'maxLength', data: 1000}], tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL }, 'requirementsAndConstrains' : { @@ -38,7 +38,7 @@ export default (state = {}, action) => { 'licenseTerm' : { isValid: true, errorText: '', - validations: [], + validations: [{type: 'required', data: true}], tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL }, 'name' : { 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 42a33fd509..a15e5daa4e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx @@ -19,7 +19,7 @@ import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; import {TabsForm as 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 Tab from 'sdc-ui/lib/react/Tab.js'; import Input from 'nfvo-components/input/validation/Input.jsx'; import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; @@ -96,7 +96,6 @@ const GeneralTabContent = ({data, genericFieldInfo, onDataChanged, validateName, overlayPos='bottom' data-test-id='create-la-description' name='license-agreement-description' - isRequired={true} type='textarea'/> </GridItem> </GridSection> @@ -145,10 +144,10 @@ class LicenseAgreementEditorView extends React.Component { 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} > + className='license-model-form license-agreement-form'> + <Tabs activeTab={onTabSelect ? selectedTab : undefined} onTabClick={onTabSelect} invalidTabs={this.props.invalidTabs} > <Tab - eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL} + tabId={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL} data-test-id='general-tab' title={i18n('General')}> <fieldset disabled={isReadOnlyMode}> @@ -157,7 +156,7 @@ class LicenseAgreementEditorView extends React.Component { </fieldset> </Tab> <Tab - eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS} + tabId={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS} data-test-id='feature-group-tab' title={i18n('Feature Groups')}> <fieldset disabled={isReadOnlyMode}> @@ -168,7 +167,7 @@ class LicenseAgreementEditorView extends React.Component { selectedValuesList={data.featureGroupsIds} availableList={featureGroupsList} onChange={ selectedValuesList => onDataChanged( { featureGroupsIds: selectedValuesList }, LA_EDITOR_FORM )}/> : - <p>{i18n('There is no available feature groups')}</p>} + <p>{i18n('There are no available feature groups')}</p>} </fieldset> </Tab> </Tabs> 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 192d2ded99..6247723d72 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx @@ -59,7 +59,7 @@ class LicenseAgreementListEditorView extends React.Component { 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 show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal license-model-modal license-agreement-modal'> <Modal.Header> <Modal.Title>{`${isModalInEditMode ? i18n('Edit License Agreement') : i18n('Create New License Agreement')}`}</Modal.Title> </Modal.Header> 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 dd2a5c6003..f5017f6d7a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js @@ -17,6 +17,8 @@ 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'; +import {actionTypes as limitEditorActions} from 'sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js'; +import getValue from 'nfvo-utils/getValue.js'; function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); @@ -36,8 +38,13 @@ function postLicenseKeyGroup(licenseModelId, licenseKeyGroup, version) { return RestAPIUtil.post(baseUrl(licenseModelId, version), { name: licenseKeyGroup.name, description: licenseKeyGroup.description, - operationalScope: licenseKeyGroup.operationalScope, - type: licenseKeyGroup.type + operationalScope: getValue(licenseKeyGroup.operationalScope), + type: licenseKeyGroup.type, + increments: licenseKeyGroup.increments, + thresholdValue: licenseKeyGroup.thresholdValue, + thresholdUnits: getValue(licenseKeyGroup.thresholdUnits), + startDate: licenseKeyGroup.startDate, + expiryDate: licenseKeyGroup.expiryDate }); } @@ -45,11 +52,50 @@ function putLicenseKeyGroup(licenseModelId, licenseKeyGroup, version) { return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${licenseKeyGroup.id}`, { name: licenseKeyGroup.name, description: licenseKeyGroup.description, - operationalScope: licenseKeyGroup.operationalScope, - type: licenseKeyGroup.type + operationalScope: getValue(licenseKeyGroup.operationalScope), + type: licenseKeyGroup.type, + increments: licenseKeyGroup.increments, + thresholdValue: licenseKeyGroup.thresholdValue, + thresholdUnits: getValue(licenseKeyGroup.thresholdUnits), + startDate: licenseKeyGroup.startDate, + expiryDate: licenseKeyGroup.expiryDate }); } +function fetchLimitsList(licenseModelId, licenseKeyGroupId, version) { + return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}/${licenseKeyGroupId}/limits`); +} + +function deleteLimit(licenseModelId, licenseKeyGroupId, version, limitId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId, version)}/${licenseKeyGroupId}/limits/${limitId}`); +} + +function postLimit(licenseModelId, licenseKeyGroupId, version, limit) { + return RestAPIUtil.post(`${baseUrl(licenseModelId, version)}/${licenseKeyGroupId}/limits`, { + name: limit.name, + type: limit.type, + description: limit.description, + metric: limit.metric, + value: limit.value, + unit: limit.unit, + aggregationFunction: getValue(limit.aggregationFunction), + time: getValue(limit.time) + }); +} + +function putLimit(licenseModelId, licenseKeyGroupId, version, limit) { + + return RestAPIUtil.put(`${baseUrl(licenseModelId, version)}/${licenseKeyGroupId}/limits/${limit.id}`, { + name: limit.name, + type: limit.type, + description: limit.description, + metric: limit.metric, + value: limit.value, + unit: limit.unit, + aggregationFunction: getValue(limit.aggregationFunction), + time: getValue(limit.time) + }); +} export default { fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}) { @@ -59,7 +105,10 @@ export default { })); }, - openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup} = {}) { + openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup, licenseModelId, version} = {}) { + if (licenseModelId && version) { + this.fetchLimits(dispatch, {licenseModelId, version, licenseKeyGroup}); + } dispatch({ type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.OPEN, licenseKeyGroup @@ -124,5 +173,34 @@ export default { LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { this.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); }); + }, + + + fetchLimits(dispatch, {licenseModelId, version, licenseKeyGroup}) { + return fetchLimitsList(licenseModelId, licenseKeyGroup.id, version).then(response => { + dispatch({ + type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.LIMITS_LIST_LOADED, + response + }); + }); + }, + + submitLimit(dispatch, {licenseModelId, version, licenseKeyGroup, limit}) { + const promise = limit.id ? putLimit(licenseModelId,licenseKeyGroup.id, version, limit) : + postLimit(licenseModelId,licenseKeyGroup.id, version, limit); + return promise.then(() => { + dispatch({ + type: limitEditorActions.CLOSE + }); + this.fetchLimits(dispatch, {licenseModelId, version, licenseKeyGroup}); + }); + }, + + deleteLimit(dispatch, {licenseModelId, version, licenseKeyGroup, limit}) { + return deleteLimit(licenseModelId,licenseKeyGroup.id, version, limit.id).then(() => { + this.fetchLimits(dispatch, {licenseModelId, version, licenseKeyGroup}); + }); } + + }; 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 50d1fe8625..c376cb3fbc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js @@ -15,7 +15,7 @@ */ 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'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; export const actionTypes = keyMirror({ @@ -28,6 +28,7 @@ export const actionTypes = keyMirror({ OPEN: null, CLOSE: null, DATA_CHANGED: null, + LIMITS_LIST_LOADED: null } }); @@ -77,3 +78,10 @@ export const getOperationalScopes = (operationalScope) => { return allOpScopes; } }; + +export const tabIds = { + GENERAL: 'GENERAL', + SP_LIMITS: 'SP_LIMITS', + VENDOR_LIMITS: 'VENDOR_LIMITS', + ADD_LIMIT_BUTTON: 'ADD_LIMIT_BUTTON' +}; 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 aef1532dc1..028fa9d3e4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js @@ -16,12 +16,13 @@ import {connect} from 'react-redux'; import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; import LicenseKeyGroupsEditorView from './LicenseKeyGroupsEditorView.jsx'; +import LimitEditorActionHelper from '../limits/LimitEditorActionHelper.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => { - let {data, genericFieldInfo, formReady} = licenseKeyGroup.licenseKeyGroupsEditor; + let {data, genericFieldInfo, formReady, limitsList} = licenseKeyGroup.licenseKeyGroupsEditor; let previousData, LKGNames = {}; const licenseKeyGroupId = data ? data.id : null; @@ -33,7 +34,7 @@ const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => { const list = licenseKeyGroup.licenseKeyGroupsList; for (let i = 0; i < list.length; i++) { - LKGNames[list[i].name] = list[i].id; + LKGNames[list[i].name.toLowerCase()] = list[i].id; } return { @@ -42,7 +43,8 @@ const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => { genericFieldInfo, isFormValid, formReady, - LKGNames + LKGNames, + limitsList }; }; @@ -50,11 +52,13 @@ const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), onCancel: () => LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch), - onSubmit: ({previousLicenseKeyGroup, licenseKeyGroup}) => { - LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch); + onSubmit: ({previousLicenseKeyGroup, licenseKeyGroup, keepOpen}) => { + if (!keepOpen) {LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch);} LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup, version}); }, - onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName), + onCloseLimitEditor: () => LimitEditorActionHelper.closeLimitsEditor(dispatch), + onOpenLimitEditor: (limit) => LimitEditorActionHelper.openLimitsEditor(dispatch, {limit}) }; }; 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 090c971c65..b1a22f3d9a 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js @@ -14,20 +14,30 @@ * permissions and limitations under the License. */ import {actionTypes, defaultState, LKG_FORM_NAME} from './LicenseKeyGroupsConstants.js'; +import moment from 'moment'; +import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js'; export default (state = {}, action) => { switch (action.type) { case actionTypes.licenseKeyGroupsEditor.OPEN: + let licenseKeyGroupData = {...action.licenseKeyGroup}; + let {startDate, expiryDate} = licenseKeyGroupData; + if (startDate) { + licenseKeyGroupData.startDate = moment(startDate, DATE_FORMAT).format(DATE_FORMAT); + } + if (expiryDate) { + licenseKeyGroupData.expiryDate = moment(expiryDate, DATE_FORMAT).format(DATE_FORMAT); + } return { ...state, - data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor, + data: action.licenseKeyGroup ? licenseKeyGroupData : defaultState.licenseKeyGroupsEditor, formReady: null, formName: LKG_FORM_NAME, genericFieldInfo: { 'description' : { isValid: true, errorText: '', - validations: [{type: 'required', data: true}, {type: 'maxLength', data: 1000}] + validations: [{type: 'maxLength', data: 1000}] }, 'name' : { isValid: true, @@ -43,9 +53,39 @@ export default (state = {}, action) => { isValid: true, errorText: '', validations: [] + }, + 'thresholdUnits' : { + isValid: true, + errorText: '', + validations: [] + }, + 'thresholdValue' : { + isValid: true, + errorText: '', + validations: [] + }, + 'increments' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 120}] + }, + 'startDate': { + isValid: true, + errorText: '', + validations: [] + }, + 'expiryDate': { + isValid: true, + errorText: '', + validations: [] } } }; + case actionTypes.licenseKeyGroupsEditor.LIMITS_LIST_LOADED: + return { + ...state, + limitsList: action.response.results + }; case actionTypes.licenseKeyGroupsEditor.CLOSE: return {}; default: 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 b95efd0f9c..647e205cb5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx @@ -17,27 +17,43 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Validator from 'nfvo-utils/Validator.js'; +import Tabs from 'sdc-ui/lib/react/Tabs.js'; +import Tab from 'sdc-ui/lib/react/Tab.js'; + +import Button from 'sdc-ui/lib/react/Button.js'; import Form from 'nfvo-components/input/validation/Form.jsx'; import Input from 'nfvo-components/input/validation/Input.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; -import {optionsInputValues as licenseKeyGroupOptionsInputValues, LKG_FORM_NAME} from './LicenseKeyGroupsConstants.js'; +import {optionsInputValues as licenseKeyGroupOptionsInputValues, LKG_FORM_NAME, tabIds} from './LicenseKeyGroupsConstants.js'; +import {optionsInputValues as LicenseModelOptionsInputValues} from '../LicenseModelConstants.js'; +import {validateStartDate, thresholdValueValidation} from '../LicenseModelValidations.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({ +import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js'; + +import LicenseKeyGroupsLimits from './LicenseKeyGroupsLimits.js'; +import {limitType, NEW_LIMIT_TEMP_ID} from '../limits/LimitEditorConstants.js'; + + const LicenseKeyGroupPropType = React.PropTypes.shape({ id: React.PropTypes.string, name: React.PropTypes.string, description: React.PropTypes.string, + increments: React.PropTypes.string, operationalScope: React.PropTypes.shape({ choices: React.PropTypes.array, other: React.PropTypes.string }), - type: React.PropTypes.string + type: React.PropTypes.string, + thresholdUnits: React.PropTypes.string, + thresholdValue: React.PropTypes.number, + startDate: React.PropTypes.string, + expiryDate: React.PropTypes.string }); -const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, validateName, validateOperationalScope}) => { - let {name, description, operationalScope, type} = data; +const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, validateName, validateStartDate, thresholdValueValidation}) => { + let {name, description, increments, operationalScope, type, thresholdUnits, thresholdValue, startDate, expiryDate} = data; return ( <GridSection> <GridItem colSpan={2}> @@ -55,11 +71,10 @@ const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, vali <InputOptions onInputChange={()=>{}} isMultiSelect={true} - isRequired={true} onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}}, - LKG_FORM_NAME, {operationalScope: validateOperationalScope})} + LKG_FORM_NAME)} onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], - other: operationalScope}}, LKG_FORM_NAME, {operationalScope: validateOperationalScope})} + other: operationalScope}}, LKG_FORM_NAME)} label={i18n('Operational Scope')} data-test-id='create-lkg-operational-scope' type='select' @@ -77,7 +92,6 @@ const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, vali value={description} isValid={genericFieldInfo.description.isValid} errorText={genericFieldInfo.description.errorText} - isRequired={true} type='textarea' overlayPos='bottom' /> </GridItem> @@ -101,6 +115,82 @@ const LicenseKeyGroupFormContent = ({data, onDataChanged, genericFieldInfo, vali } </Input> </GridItem> + <GridItem> + <Input + onChange={e => { + // setting the unit to the correct value + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({thresholdUnits: val}, LKG_FORM_NAME); + // TODO make sure that the value is valid too + onDataChanged({thresholdValue: thresholdValue}, LKG_FORM_NAME,{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' > + {LicenseModelOptionsInputValues.THRESHOLD_UNITS.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + className='entitlement-pools-form-row-threshold-value' + onChange={thresholdValue => onDataChanged({thresholdValue}, LKG_FORM_NAME, + {thresholdValue : thresholdValueValidation})} + label={i18n('Threshold Value')} + isValid={genericFieldInfo.thresholdValue.isValid} + errorText={genericFieldInfo.thresholdValue.errorText} + data-test-id='create-ep-threshold-value' + value={thresholdValue} + type='text'/> + </GridItem> + <GridItem> + <Input + type='date' + label={i18n('Start Date')} + value={startDate} + dateFormat={DATE_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={startDate => onDataChanged( + {startDate: startDate ? startDate.format(DATE_FORMAT) : ''}, + LKG_FORM_NAME, + {startDate: validateStartDate} + )} + isValid={genericFieldInfo.startDate.isValid} + errorText={genericFieldInfo.startDate.errorText} + selectsStart/> + </GridItem> + <GridItem> + <Input + type='date' + label={i18n('Expiry Date')} + value={expiryDate} + dateFormat={DATE_FORMAT} + startDate={startDate} + endDate={expiryDate} + onChange={expiryDate => { + onDataChanged({expiryDate: expiryDate ? expiryDate.format(DATE_FORMAT) : ''}, LKG_FORM_NAME); + onDataChanged({startDate}, LKG_FORM_NAME, {startDate: validateStartDate}); + }} + isValid={genericFieldInfo.expiryDate.isValid} + errorText={genericFieldInfo.expiryDate.errorText} + selectsEnd/> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={increments => onDataChanged({increments}, LKG_FORM_NAME)} + label={i18n('Increments')} + value={increments} + data-test-id='create-ep-increments' + type='text'/> + </GridItem> </GridSection> ); }; @@ -120,36 +210,100 @@ class LicenseKeyGroupsEditorView extends React.Component { data: {} }; + componentDidUpdate(prevProps) { + if (this.props.formReady && this.props.formReady !== prevProps.formReady) { // if form validation succeeded -> continue with submit + this.submit(); + } + } + + state = { + localFeatureGroupsListFilter: '', + selectedTab: tabIds.GENERAL, + selectedLimit: '' + }; + render() { - let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo} = this.props; + let {data = {}, onDataChanged, isReadOnlyMode, onCloseLimitEditor, genericFieldInfo, limitsList = []} = this.props; + let {selectedTab} = this.state; + const isTabsDisabled = !data.id || !this.props.isFormValid; return ( - <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'> - <LicenseKeyGroupFormContent - data={data} - onDataChanged={onDataChanged} - genericFieldInfo={genericFieldInfo} - validateName={(value)=> this.validateName(value)} - validateOperationalScope={this.validateOperationalScope}/> - </Form>} + <div className='license-keygroup-editor'> + <Tabs + type='menu' + activeTab={selectedTab} + onTabClick={(tabIndex)=>{ + if (tabIndex === tabIds.ADD_LIMIT_BUTTON) { + this.onAddLimit(); + } else { + this.setState({selectedTab: tabIndex}); + onCloseLimitEditor(); + this.setState({selectedLimit: ''}); + } + }} + invalidTabs={[]}> + <Tab tabId={tabIds.GENERAL} data-test-id='general-tab' title={i18n('General')}> + { genericFieldInfo && + <Form + ref='validationForm' + hasButtons={false} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.props.onValidateForm(LKG_FORM_NAME) } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + className='license-model-form license-key-groups-form'> + <LicenseKeyGroupFormContent + data={data} + onDataChanged={onDataChanged} + genericFieldInfo={genericFieldInfo} + validateName={(value)=> this.validateName(value)} + validateStartDate={(value, state)=> validateStartDate(value, state)} + thresholdValueValidation={(value, state) => thresholdValueValidation(value, state)}/> + </Form>} + + </Tab> + <Tab tabId={tabIds.SP_LIMITS} disabled={isTabsDisabled} data-test-id='general-tab' title={i18n('SP Limits')}> + {selectedTab === tabIds.SP_LIMITS && + <LicenseKeyGroupsLimits + limitType={limitType.SERVICE_PROVIDER} + limitsList={limitsList.filter(item => item.type === limitType.SERVICE_PROVIDER)} + selectedLimit={this.state.selectedLimit} + onCloseLimitEditor={() => this.onCloseLimitEditor()} + onSelectLimit={limit => this.onSelectLimit(limit)}/>} + </Tab> + <Tab tabId={tabIds.VENDOR_LIMITS} disabled={isTabsDisabled} data-test-id='general-tab' title={i18n('Vendor Limits')}> + {selectedTab === tabIds.VENDOR_LIMITS && + <LicenseKeyGroupsLimits + limitType={limitType.VENDOR} + limitsList={limitsList.filter(item => item.type === limitType.VENDOR)} + selectedLimit={this.state.selectedLimit} + onCloseLimitEditor={() => this.onCloseLimitEditor()} + onSelectLimit={limit => this.onSelectLimit(limit)}/>} + </Tab> + {selectedTab !== tabIds.GENERAL ? + <Button disabled={this.state.selectedLimit} className='add-limit-button' tabId={tabIds.ADD_LIMIT_BUTTON} btnType='link' iconName='plus'>{i18n('Add Limit')}</Button> : + <div></div> // Render empty div to not break tabs + } + </Tabs> + + <GridSection className='license-model-modal-buttons license-key-group-editor-buttons'> + {!this.state.selectedLimit && <Button btnType='default' disabled={!this.props.isFormValid} onClick={() => this.submit()} type='reset'>{i18n('Save')}</Button>} + <Button btnType={this.state.selectedLimit ? 'default' : 'outline'} onClick={() => this.props.onCancel()} type='reset'> + {i18n('Cancel')} + </Button> + </GridSection> </div> + ); } submit() { - const {data: licenseKeyGroup, previousData: previousLicenseKeyGroup} = this.props; - this.props.onSubmit({licenseKeyGroup, previousLicenseKeyGroup}); + const {data: licenseKeyGroup, previousData: previousLicenseKeyGroup, formReady, onValidateForm, onSubmit} = this.props; + if (!formReady) { + onValidateForm(LKG_FORM_NAME); + } else { + onSubmit({licenseKeyGroup, previousLicenseKeyGroup}); + } } validateName(value) { @@ -160,27 +314,23 @@ class LicenseKeyGroupsEditorView extends React.Component { {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: '' - }; - } - } + onSelectLimit(limit) { + if (limit.id === this.state.selectedLimit) { + this.setState({selectedLimit: ''}); + return; } - return { - isValid: false, - errorText: 'Field is required' - }; + this.setState({selectedLimit: limit.id}); + this.props.onOpenLimitEditor(limit); + } + + onCloseLimitEditor() { + this.setState({selectedLimit: ''}); + this.props.onCloseLimitEditor(); + } + + onAddLimit() { + this.setState({selectedLimit: NEW_LIMIT_TEMP_ID}); + this.props.onOpenLimitEditor(); } } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsLimits.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsLimits.js new file mode 100644 index 0000000000..7745a12fec --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsLimits.js @@ -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 {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {actionTypes as globalModalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import Limits from 'sdc-app/onboarding/licenseModel/limits/Limits.jsx'; + +import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; + +const mapStateToProps = ({licenseModel: {licenseKeyGroup: {licenseKeyGroupsEditor: {data}}, limitEditor}, currentScreen}) => { + let {props: {licenseModelId, version}} = currentScreen; + return { + parent: data, + limitEditor, + licenseModelId, + version + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), + onSubmit: (limit, licenseKeyGroup, licenseModelId, version) => LicenseKeyGroupsActionHelper.submitLimit(dispatch, + { + limit, + licenseKeyGroup, + licenseModelId, + version}), + onDelete: ({limit, parent, licenseModelId, version, onCloseLimitEditor, selectedLimit}) => dispatch({ + type: globalModalActionTypes.GLOBAL_MODAL_WARNING, + data:{ + msg: i18n(`Are you sure you want to delete ${limit.name}?`), + confirmationButtonText: i18n('Delete'), + title: i18n('Warning'), + onConfirmed: ()=> LicenseKeyGroupsActionHelper.deleteLimit(dispatch, {limit, licenseKeyGroup: parent, licenseModelId, version}).then(() => + selectedLimit === limit.id && onCloseLimitEditor() + ) + } + }) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(Limits);
\ No newline at end of file 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 e2c6c30e21..a8cf1eb0a1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js @@ -38,7 +38,7 @@ const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) const mapActionsToProps = (dispatch, {licenseModelId, version}) => { return { onAddLicenseKeyGroupClick: () => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch), - onEditLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup}), + onEditLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup, licenseModelId, version}), onDeleteLicenseKeyGroupClick: licenseKeyGroup => dispatch({ type: globalMoadlActions.GLOBAL_MODAL_WARNING, data:{ 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 b8ccd68bce..1a7f2b0b5b 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx @@ -21,7 +21,7 @@ import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; import LicenseKeyGroupsEditor from './LicenseKeyGroupsEditor.js'; -import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; import {optionsInputValues} from './LicenseKeyGroupsConstants'; class LicenseKeyGroupsListEditorView extends React.Component { @@ -61,7 +61,7 @@ class LicenseKeyGroupsListEditorView extends React.Component { isReadOnlyMode={isReadOnlyMode}> {this.filterList().map(licenseKeyGroup => (this.renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode)))} </ListEditorView> - <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal license-key-groups-modal'> + <Modal show={isDisplayModal} bsSize='large' animation={true} className='onborading-modal license-model-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> @@ -122,16 +122,20 @@ class LicenseKeyGroupsListEditorView extends React.Component { } getOperationalScopes(operationalScope) { - if(operationalScope.choices.toString() === i18n(optionInputOther.OTHER) && operationalScope.other !== '') { + + if(operationalScope.choices && operationalScope.choices.toString() === i18n(optionInputOther.OTHER)) { return operationalScope.other; } - else { + else if (operationalScope.choices) { let allOpScopes = ''; for (let opScope of operationalScope.choices) { allOpScopes += allOpScopes === '' ? InputOptions.getTitleByName(optionsInputValues, opScope) : `, ${InputOptions.getTitleByName(optionsInputValues, opScope)}`; } return allOpScopes; - } + } + else { + return ''; + } } extractValue(item) { diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.js new file mode 100644 index 0000000000..d483383472 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.js @@ -0,0 +1,25 @@ +import {connect} from 'react-redux'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import LimitEditor from './LimitEditor.jsx'; + +const mapStateToProps = ({licenseModel: {limitEditor}}) => { + + let {data, genericFieldInfo, formReady} = limitEditor; + let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); + + return { + data, + genericFieldInfo, + isFormValid, + formReady + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: (deltaData, formName, customValidations) => ValidationHelper.dataChanged(dispatch, {deltaData, formName, customValidations}), + onValidateForm: (formName) => ValidationHelper.validateForm(dispatch, formName) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LimitEditor);
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx new file mode 100644 index 0000000000..f70f917725 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx @@ -0,0 +1,193 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {LIMITS_FORM_NAME, selectValues} from './LimitEditorConstants.js'; +import Button from 'sdc-ui/lib/react/Button.js'; +import Validator from 'nfvo-utils/Validator.js'; + +const LimitPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + metric: React.PropTypes.string, + value: React.PropTypes.number, + aggregationFunction: React.PropTypes.string, + time: React.PropTypes.string, + unit: React.PropTypes.number +}); + +class LimitEditor extends React.Component { + static propTypes = { + data: LimitPropType, + limitsNames: React.PropTypes.object, + isReadOnlyMode: React.PropTypes.bool, + isFormValid: React.PropTypes.bool, + formReady: React.PropTypes.bool, + genericFieldInfo: React.PropTypes.object.isRequired, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onValidateForm: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + componentDidUpdate(prevProps) { + if (this.props.formReady && this.props.formReady !== prevProps.formReady) { + this.submit(); + } + } + + render() { + let {data = {}, onDataChanged, isReadOnlyMode, genericFieldInfo, onCancel, isFormValid, formReady, onValidateForm} = this.props; + let {name, description, metric, value, aggregationFunction, time, unit} = data; + return ( + <div className='limit-editor'> + {!data.id && + <div className='limit-editor-title'> + {data.name ? data.name : i18n('NEW LIMIT')} + </div>} + { + genericFieldInfo && + <Form + ref='validationForm' + hasButtons={false} + isValid={isFormValid} + formReady={formReady} + onValidateForm={() => onValidateForm(LIMITS_FORM_NAME) } + labledButtons={false} + isReadOnlyMode={isReadOnlyMode} + className='limit-editor-form'> + <GridSection className='limit-editor-form-grid-section'> + <GridItem colSpan={2}> + <Input + onChange={name => onDataChanged({name}, LIMITS_FORM_NAME, {name: () => this.validateName(name)})} + label={i18n('Name')} + data-test-id='limit-editor-name' + value={name} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} + isRequired={true} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={description => onDataChanged({description}, LIMITS_FORM_NAME)} + label={i18n('Description')} + data-test-id='limit-editor-description' + value={description} + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + isRequired={false} + type='text'/> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={e => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({metric: val}, LIMITS_FORM_NAME);} + } + isRequired={true} + value={metric} + label={i18n('Metric')} + data-test-id='limit-editor-metric' + isValid={genericFieldInfo.metric.isValid} + errorText={genericFieldInfo.metric.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + {selectValues.METRIC.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + onChange={value => onDataChanged({value}, LIMITS_FORM_NAME)} + label={i18n('Metric value')} + data-test-id='limit-editor-metric-value' + value={value} + isValid={genericFieldInfo.value.isValid} + errorText={genericFieldInfo.value.errorText} + isRequired={true} + type='number'/> + </GridItem> + <GridItem> + <Input + onChange={unit => onDataChanged({unit}, LIMITS_FORM_NAME)} + label={i18n('Units')} + data-test-id='limit-editor-units' + value={unit} + isValid={genericFieldInfo.unit.isValid} + errorText={genericFieldInfo.unit.errorText} + isRequired={false} + type='number'/> + </GridItem> + <GridItem colSpan={2}> + <Input + onChange={e => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({aggregationFunction: val}, LIMITS_FORM_NAME);} + } + value={aggregationFunction} + label={i18n('Aggregation Function')} + data-test-id='limit-editor-aggregation-function' + isValid={genericFieldInfo.aggregationFunction.isValid} + errorText={genericFieldInfo.aggregationFunction.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + {selectValues.AGGREGATION_FUNCTION.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + <GridItem> + <Input + onChange={e => { + const selectedIndex = e.target.selectedIndex; + const val = e.target.options[selectedIndex].value; + onDataChanged({time: val}, LIMITS_FORM_NAME);} + } + value={time} + label={i18n('Time')} + data-test-id='limit-editor-time' + isValid={genericFieldInfo.time.isValid} + errorText={genericFieldInfo.time.errorText} + groupClassName='bootstrap-input-options' + className='input-options-select' + type='select' > + {selectValues.TIME.map(mtype => + <option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)} + </Input> + </GridItem> + </GridSection> + <GridSection className='limit-editor-buttons'> + <Button btnType='outline' disabled={!isFormValid} onClick={() => this.submit()} type='reset'>{i18n('Save')}</Button> + <Button btnType='outline' color='gray' onClick={onCancel} type='reset'>{i18n('Cancel')}</Button> + </GridSection> + </Form> + } + </div> + ); + } + + validateName(value) { + const {data: {id}, limitsNames} = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({itemId: id, itemName: value, list: limitsNames}); + + return !isExists ? {isValid: true, errorText: ''} : + {isValid: false, errorText: i18n('Limit by the name \'' + value + '\' already exists. Limit name must be unique')}; + } + + submit() { + if (!this.props.formReady) { + this.props.onValidateForm(LIMITS_FORM_NAME); + } else { + this.props.onSubmit(); + } + } +} + +export default LimitEditor;
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorActionHelper.js new file mode 100644 index 0000000000..09c64ada62 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorActionHelper.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} from './LimitEditorConstants.js'; + + +const LimitEditorActionHelper = { + openLimitsEditor(dispatch, {limit}) { + dispatch({type: actionTypes.OPEN, limitItem: limit}); + }, + + closeLimitsEditor(dispatch) { + dispatch({type: actionTypes.CLOSE}); + } +}; + +export default LimitEditorActionHelper;
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js new file mode 100644 index 0000000000..1bef286442 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js @@ -0,0 +1,52 @@ +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({ + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, +}); + +export const LIMITS_FORM_NAME = 'LIMITSFORM'; + +export const selectValues = { + METRIC: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Software_Instances_Count', title: 'Software Instances'}, + {enum: 'Core', title: 'Core'}, + {enum: 'CPU', title: 'CPU'}, + {enum: 'Trunks', title: 'Trunks'}, + {enum: 'User', title: 'User'}, + {enum: 'Subscribers', title: 'Subscribers'}, + {enum: 'Tenants', title: 'Tenants'}, + {enum: 'Tokens', title: 'Tokens'}, + {enum: 'Seats', title: 'Seats'}, + {enum: 'Units_TB', title: 'Units-TB'}, + {enum: 'Units_GB', title: 'Units-GB'}, + {enum: 'Units_MB', title: 'Units-MB'} + ], + AGGREGATION_FUNCTION: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Peak', title: 'Peak'}, + {enum: 'Average', title: 'Average'} + ], + TIME: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Hour', title: 'Hour'}, + {enum: 'Day', title: 'Day'}, + {enum: 'Month', title: 'Month'} + ] + +}; + +export const limitType = { + SERVICE_PROVIDER: 'ServiceProvider', + VENDOR: 'Vendor' +}; + +export const defaultState = { + LIMITS_EDITOR_DATA: {} +}; + +export const NEW_LIMIT_TEMP_ID = 'NEW_LIMIT_TEMP_ID';
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js new file mode 100644 index 0000000000..2499093af2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js @@ -0,0 +1,70 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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, LIMITS_FORM_NAME, defaultState} from './LimitEditorConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.OPEN: + return { + ...state, + data: action.limitItem ? {...action.limitItem} : defaultState.LIMITS_EDITOR_DATA, + formReady: null, + formName: LIMITS_FORM_NAME, + genericFieldInfo: { + 'description' : { + isValid: true, + errorText: '', + validations: [{type: 'maxLength', data: 1000}] + }, + 'name' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'maxLength', data: 120}] + }, + 'metric' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'value' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}, {type: 'numeric', data: true}, {type: 'minimum', data: 0}] + }, + 'unit' : { + isValid: true, + errorText: '', + validations: [{type: 'numeric', data: true}] + }, + 'aggregationFunction' : { + isValid: true, + errorText: '', + validations: [] + }, + 'time' : { + isValid: true, + errorText: '', + validations: [] + } + } + }; + case actionTypes.CLOSE: + return {}; + default: + return state; + } +};
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/Limits.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/Limits.jsx new file mode 100644 index 0000000000..ec5a1dff72 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/Limits.jsx @@ -0,0 +1,108 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 LimitEditor from './LimitEditor.js'; +import {NEW_LIMIT_TEMP_ID, selectValues} from './LimitEditorConstants.js'; + +const LimitItem = ({isReadOnlyMode, limit, onDelete, onSelect}) => { + const {name, description, metric, value, aggregationFunction = '', time = ''} = limit; + const timeLabel = time ? `per ${time}` : ''; + return ( + <ListEditorItemView + onDelete={onDelete} + onSelect={onSelect} + isReadOnlyMode={isReadOnlyMode}> + <div className='list-editor-item-view-field limit-name'> + <div className='text name'>{name}</div> + </div> + + <div className='list-editor-item-view-field limit-description'> + <div className='text description'>{description}</div> + </div> + + <div className='list-editor-item-view-field limit-metric-details'> + <div className='text description'>{`${selectValues.METRIC.find(item => item.enum === metric).title} ${value} ${aggregationFunction} ${timeLabel}`}</div> + </div> + </ListEditorItemView> + ); +}; + +class Limits extends React.Component { + + + state = { + localFilter: '' + }; + + render() { + const {isReadOnlyMode = false, limitEditor, limitsList = [], onCloseLimitEditor, selectedLimit} = this.props; + let limitsNames = {}; + for (let i = 0; i < limitsList.length; i++) { + limitsNames[limitsList[i].name.toLowerCase()] = limitsList[i].id; + } + return ( + <div className='license-model-limits-view'> + <ListEditorView + isReadOnlyMode={isReadOnlyMode}> + {this.props.selectedLimit === NEW_LIMIT_TEMP_ID && limitEditor.data && + <LimitEditor limitsNames={limitsNames} onCancel={onCloseLimitEditor} onSubmit={ () => this.submit()}/> + } + {limitsList.length === 0 && !limitEditor.data && <div className='no-limits-text'>{i18n('There are no limits')}</div>} + {limitsList.map(limit => + <div key={limit.id} className='limit-item-wrapper'> + <LimitItem + onDelete={() => this.delete(limit)} + onSelect={selectedLimit ? undefined : () => this.props.onSelectLimit(limit)} + clickable={!selectedLimit} + isReadOnlyMode={isReadOnlyMode} + limit={limit}/> + {limit.id === selectedLimit && limitEditor.data && <LimitEditor limitsNames={limitsNames} onCancel={onCloseLimitEditor} onSubmit={ () => this.submit()}/>} + </div> )} + </ListEditorView> + + </div> + ); + } + + submit() { + let {onSubmit, onCloseLimitEditor, parent, limitEditor, licenseModelId, version, limitType} = this.props; + onSubmit({type: limitType, ...limitEditor.data}, parent, licenseModelId, version).then(() => onCloseLimitEditor()); + } + + delete(limit) { + let {onDelete, parent, licenseModelId, version, onCloseLimitEditor, selectedLimit} = this.props; + onDelete({limit, parent, licenseModelId, version, onCloseLimitEditor, selectedLimit}); + } + + filterList() { + let {limitsList = []} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return limitsList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return limitsList; + } + } +} + +export default Limits; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitsServer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitsServer.js new file mode 100644 index 0000000000..1b8ecb9d94 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitsServer.js @@ -0,0 +1,46 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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. + */ + +// items/{itemId}/users + +let list = [ + +]; + +export default { + fetch() { + return Promise.resolve({ + listCount: list.length, + results: list + }); + }, + + put(url, payload) { + // let {removedUsers, addedUsers} = payload; + // users = users.filter(user => !removedUsers.map(user => user.userId).includes(user.userId)).concat(addedUsers); + payload.id = Math.random() * (1000 - 1) + 1; + list.push(payload); + return Promise.resolve(); + }, + + destroy(url) { + const parts = url.split('/'); + const id = parts[parts.length - 1]; + let newList = list.filter(item => item.id !== id); + list = newList; + return Promise.resolve(); + } +};
\ No newline at end of file diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js index b5a27ed018..56b23384a7 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewConstants.js @@ -34,9 +34,9 @@ export const actionTypes = keyMirror({ LM_DATA_CHANGED: null }); -export const selectedButton = keyMirror({ - VLM_LIST_VIEW: null, - NOT_IN_USE: null -}); +export const selectedButton = { + VLM_LIST_VIEW: 'VLM_LIST_VIEW', + NOT_IN_USE: 'NOT_IN_USE' +}; 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 index d6c79ddb52..15f6c2d1bb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/LicenseModelOverviewView.jsx @@ -52,7 +52,7 @@ class LicenseModelOverviewView extends React.Component { licenseModelId: React.PropTypes.string, licensingDataList: React.PropTypes.array, modalHeader: React.PropTypes.string, - selectedTab: React.PropTypes.symbol, + selectedTab: React.PropTypes.string, onTabSelect: React.PropTypes.func, onCallVCAction: React.PropTypes.func, onClose: React.PropTypes.func @@ -67,14 +67,13 @@ class LicenseModelOverviewView extends React.Component { <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}/> + <ListButtons onTabSelect={onTabSelect} selectedTab={selectedTab}/> </div> <VLMListView licensingDataList={licensingDataList} showInUse={selectedInUse}/> </div> { isDisplayModal && - <Modal show={isDisplayModal} bsSize='large' animation={true} className={classNames('onborading-modal', setModalClassName(modalHeader))}> + <Modal show={isDisplayModal} bsSize='large' animation={true} className={classNames('onborading-modal license-model-modal', setModalClassName(modalHeader))}> <Modal.Header> <Modal.Title>{`${i18n('Create New ')}${i18n(modalHeader)}`}</Modal.Title> </Modal.Header> diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx index 119008a849..ec05e37681 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/VLMListView.jsx @@ -66,7 +66,8 @@ class VLMListView extends Component { <LicenseAgreement laData={licenseAgreement} isCollapsed={this.state[licenseAgreement.id]} - onClick={event => this.updateCollapsable(event, licenseAgreement.id) }/> + onClick={event => this.updateCollapsable(event, licenseAgreement.id) } + isOrphan={!this.props.showInUse}/> <Collapse in={this.state[licenseAgreement.id]}> <ul> {licenseAgreement.children && licenseAgreement.children.map(item => this.renderLicensingItem(item))} @@ -83,7 +84,8 @@ class VLMListView extends Component { <FeatureGroup fgData={featureGroup} isCollapsed={this.state[featureGroup.id]} - onClick={event=> this.updateCollapsable(event, featureGroup.id) }/> + onClick={event=> this.updateCollapsable(event, featureGroup.id) } + isOrphan={!this.props.showInUse}/> { showInUse && <Collapse in={this.state[featureGroup.id]}> <ul> @@ -99,7 +101,7 @@ class VLMListView extends Component { renderEntitlementPoolItem(entitlementPool) { return ( <li key={entitlementPool.id}> - <EntitlementPool epData={entitlementPool} /> + <EntitlementPool epData={entitlementPool} isOrphan={!this.props.showInUse}/> </li> ); } @@ -107,7 +109,7 @@ class VLMListView extends Component { renderLicenseKeyGroupItem(licenseKeyGroup) { return ( <li key={licenseKeyGroup.id}> - <LicenseKeyGroup lkgData={licenseKeyGroup} /> + <LicenseKeyGroup lkgData={licenseKeyGroup} isOrphan={!this.props.showInUse}/> </li> ); } 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 index 94977b40d1..34d2ee6db5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/EntitlementPool.jsx @@ -15,7 +15,7 @@ */ import React, {Component} from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import {extractValue, extractUnits} from '../../entitlementPools/EntitlementPoolsConstants.js'; +import {extractUnits} from '../../entitlementPools/EntitlementPoolsConstants.js'; import ArrowCol from './listItemsComponents/ArrowCol.jsx'; import ItemInfo from './listItemsComponents/ItemInfo.jsx'; import IconCol from './listItemsComponents/IconCol.jsx'; @@ -23,31 +23,23 @@ import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/Ad class EntitlementPool extends Component { render() { - let {epData: {name, description, manufacturerReferenceNumber}} = this.props; + let {epData: {name, description, thresholdValue, thresholdUnits}, isOrphan} = this.props; return ( - <div className='vlm-list-item vlm-list-item-ep' data-test-id='vlm-list-item-ep'> - <ArrowCol/> - <IconCol className='ep-icon'/> + <div className={`vlm-list-item vlm-list-item-ep ${isOrphan ? 'orphan-list-item' : ''}`} data-test-id='vlm-list-item-ep'> + {!isOrphan && <ArrowCol/>} + <IconCol className='ep-icon' text='EP'/> <ItemInfo name={name} description={description}/> <AdditionalDataCol> - <AdditionalDataElement + {thresholdValue && <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'/> + value={`${thresholdValue} ${extractUnits(thresholdUnits)}`}/> + } </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 index 8dbd46a29e..6d97dea358 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/FeatureGroup.jsx @@ -18,30 +18,41 @@ import {overviewEditorHeaders} from '../LicenseModelOverviewConstants.js'; import ArrowCol from './listItemsComponents/ArrowCol.jsx'; import ItemInfo from './listItemsComponents/ItemInfo.jsx'; import IconCol from './listItemsComponents/IconCol.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/AdditionalDataCol.jsx'; class FeatureGroup extends Component { render() { - let {fgData: {name, description, children = []}, isCollapsed, onClick} = this.props; + let {fgData: {name, manufacturerReferenceNumber, description, children = []}, isCollapsed, onClick, isOrphan} = 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'/> + <div + onClick={e => onClick(e)} + className={`vlm-list-item vlm-list-item-fg ${isOrphan ? 'orphan-list-item' : ''} ${children.length && !isOrphan ? 'clickable' : ''}`} data-test-id='vlm-list-item-fg'> + {!isOrphan && <ArrowCol isCollapsed={isCollapsed} length={children.length} />} + <IconCol className='fg-icon' text='FG'/> <ItemInfo name={name} description={description}> <div className='children-count'> <span className='count-value'> - Entitlement Pools: + {i18n('Entitlement Pools: ')} <span data-test-id='vlm-list-ep-count-value'> {`${children.filter(child => child.itemType === overviewEditorHeaders.ENTITLEMENT_POOL).length}`} </span> </span> + <span className='fg-pipeline-separator'>|</span> <span className='count-value'> - License Key Groups: + {i18n('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> + <AdditionalDataCol> + <AdditionalDataElement + name={i18n('Manufacturer Reference Number')} + value={manufacturerReferenceNumber} + className='vlm-list-item-sku'/> + </AdditionalDataCol> </div> ); } 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 index dd4686d330..dfbbe20d69 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseAgreement.jsx @@ -15,7 +15,7 @@ */ 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 InputOptions, {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; import {optionsInputValues} from '../../licenseAgreement/LicenseAgreementConstants.js'; import ArrowCol from './listItemsComponents/ArrowCol.jsx'; import ItemInfo from './listItemsComponents/ItemInfo.jsx'; @@ -24,14 +24,17 @@ import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/Ad class LicenseAgreement extends Component { render() { - let {laData: {name, description, licenseTerm, children = []}, isCollapsed, onClick} = this.props; + let {laData: {name, description, licenseTerm, children = []}, isCollapsed, onClick, isOrphan} = 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'/> + <div + onClick={e => onClick(e)} + className={`vlm-list-item vlm-list-item-la ${isOrphan ? 'orphan-list-item' : ''} ${children.length && !isOrphan ? 'clickable' : ''} `} + data-test-id='vlm-list-la-item'> + {!isOrphan && <ArrowCol isCollapsed={isCollapsed} length={children.length} />} + <IconCol className='la-icon' text='LA'/> <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> + <span className='count-value'>{i18n('Feature Groups: ')}<span data-test-id='vlm-list-fg-count-value'>{`${children.length}`}</span></span> </div> </ItemInfo> <AdditionalDataCol> 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 index 9722b83336..b1036aae23 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/listItems/LicenseKeyGroup.jsx @@ -23,17 +23,17 @@ import {AdditionalDataCol, AdditionalDataElement} from './listItemsComponents/Ad class LicenseKeyGroup extends Component { render() { - let {lkgData: {name, description, operationalScope, type}} = this.props; + let {lkgData: {name, description, operationalScope, type}, isOrphan} = this.props; return ( - <div className='vlm-list-item vlm-list-item-lkg' data-test-id='vlm-list-item-lkg'> - <ArrowCol/> - <IconCol className='lkg-icon'/> + <div className={`vlm-list-item vlm-list-item-lkg ${isOrphan ? 'orphan-list-item' : ''}`} data-test-id='vlm-list-item-lkg'> + {!isOrphan && <ArrowCol/>} + <IconCol className='lkg-icon' text='LKG'/> <ItemInfo name={name} description={description}/> <AdditionalDataCol> - <AdditionalDataElement + {operationalScope && operationalScope.choices && <AdditionalDataElement className='vlm-list-item-operational-scope' name={i18n('Operational Scope')} - value={operationalScope && getOperationalScopes(operationalScope)}/> + value={getOperationalScopes(operationalScope)}/>} <AdditionalDataElement className='vlm-list-item-group-type' name={i18n('Type')} 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 index 7fd7fcb88a..826248e510 100644 --- 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 @@ -15,10 +15,10 @@ */ import React from 'react'; -function IconCol({className}) { +function IconCol({className, text}) { return ( <div className='list-item-section list-item-icon-col'> - <div className={className}></div> + <div className={`overview-list-icon ${className ? className : ''}`}>{text}</div> </div> ); } 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 index 730ccb33f1..3d81aa5d5c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/ListButtons.jsx @@ -15,19 +15,26 @@ */ import React from 'react'; import {selectedButton} from '../LicenseModelOverviewConstants.js'; +import Tabs from 'sdc-ui/lib/react/Tabs.js'; +import Tab from 'sdc-ui/lib/react/Tab.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; -function ListButtons ({onTabSelect, selectedInUse}) { +function ListButtons ({onTabSelect, selectedTab}) { 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> + <Tabs + onTabClick={(tabId) => onTabSelect(tabId)} + activeTab={selectedTab} + className='overview-buttons-section' + type='header' > + <Tab + tabId={selectedButton.VLM_LIST_VIEW} + title={i18n('Connections List')} + data-test-id='vlm-overview-vlmlist-tab' /> + <Tab + tabId={selectedButton.NOT_IN_USE} + title={i18n('Orphans List')} + data-test-id='vlm-overview-orphans-tab' /> + </Tabs> ); } 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 index 1d65ab9869..87559901b3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/VendorDataView.js @@ -13,9 +13,11 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ -import React from 'react'; +import React, {Component} from 'react'; import {connect} from 'react-redux'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import licenseModelOverviewActionHelper from '../licenseModelOverviewActionHelper.js'; import LicenseModelActionHelper from '../../LicenseModelActionHelper.js'; @@ -46,9 +48,7 @@ const mapActionsToProps = (dispatch) => { }; }; - - -export class VendorDataView extends React.Component { +export class VendorDataView extends Component { render() { let {data: {vendorName}, description, isReadOnlyMode} = this.props; return ( @@ -62,13 +62,16 @@ export class VendorDataView extends React.Component { ); } + 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 onClick={() => {if (!isReadOnlyMode) {onVendorDescriptionEdit(description);}}} className={!isReadOnlyMode ? 'vendor-description' : 'vendor-description-readonly'}> + {this.renderOverlay( + <div className='description-data' data-test-id='vlm-summary-vendor-description'> + {description} + </div> + )} </div> ); } @@ -80,7 +83,21 @@ export class VendorDataView extends React.Component { ); } + renderOverlay(children) { + let {data: {description}, isReadOnlyMode} = this.props; + if (isReadOnlyMode) { + return ( + <OverlayTrigger + placement='bottom' + overlay={<Tooltip className='vendor-description-tooltip' id='tooltip-bottom'>{description}</Tooltip>} + delayShow={400}> + {children} + </OverlayTrigger> + ); + } + return children; + } + } export default connect(mapStateToProps, mapActionsToProps)(VendorDataView); - |