diff options
author | svishnev <shlomo-stanisla.vishnevetskiy@amdocs.com> | 2018-04-15 09:06:57 +0300 |
---|---|---|
committer | Einav Keidar <einavw@amdocs.com> | 2018-04-15 07:55:06 +0000 |
commit | ea5e43cc939f2010b4f4c97cb8d346c91348fbba (patch) | |
tree | 23e0d347103d16099ec3ca657ab246088cf88d01 /openecomp-ui/src | |
parent | 894285bfa9ccacde35b1e94e07856b53971e2559 (diff) |
Onboarding filter
Issue-ID: SDC-1187
Change-Id: I74ce464c8ee4060c381b094d26d1ded270cdf40d
Signed-off-by: svishnev <shlomo-stanisla.vishnevetskiy@amdocs.com>
Diffstat (limited to 'openecomp-ui/src')
38 files changed, 1410 insertions, 392 deletions
diff --git a/openecomp-ui/src/nfvo-components/accordion/Accordion.jsx b/openecomp-ui/src/nfvo-components/accordion/Accordion.jsx deleted file mode 100644 index 72f8de0d23..0000000000 --- a/openecomp-ui/src/nfvo-components/accordion/Accordion.jsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2016-2018 European Support Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this 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 SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; -import PropTypes from 'prop-types'; - -class Accordion extends React.Component { - static propTypes = { - title: PropTypes.string, - children: PropTypes.node - }; - - constructor(props) { - super(props); - this.state = { - open: false - }; - } - render() { - const { children, title } = this.props; - const { open } = this.state; - return ( - <div className="accordion"> - <div - onClick={() => this.setState({ open: !open })} - className="accordion-header"> - <SVGIcon - name="chevronUp" - iconClassName={open ? 'down' : ''} - /> - <div className="title">{title}</div> - </div> - <div className={`accordion-body ${open ? 'open' : ''}`}> - {children} - </div> - </div> - ); - } -} - -export default Accordion; diff --git a/openecomp-ui/src/nfvo-utils/i18n/en.json b/openecomp-ui/src/nfvo-utils/i18n/en.json index 347a8ed71c..cbc2031b4b 100644 --- a/openecomp-ui/src/nfvo-utils/i18n/en.json +++ b/openecomp-ui/src/nfvo-utils/i18n/en.json @@ -83,7 +83,8 @@ "This software product successfully submitted": "This software product successfully submitted", "Submit Failed": "Submit Failed", "Vendor Name": "Vendor Name", - "License model by the name \\": "License model by the name \\", + "License model by the name": "License model by the name", + "License model name must be unique": "License model name must be unique", "please select…": "please select…", "Warning": "Warning", "Operational Scope": "Operational Scope", @@ -160,7 +161,9 @@ "Vendor": "Vendor", "Category": "Category", "please select...": "please select...", - "Software product by the name \\": "Software product by the name \\", + "Software product by the name": "Software product by the name", + "Software product name must be unique": "Software product name must be unique", + "already exists": "already exists", "Onboarding procedure": "Onboarding procedure", "HEAT file": "HEAT file", "Manual": "Manual", @@ -356,7 +359,12 @@ "Granted": "Granted", "Taken": "Taken", "Permission": "Permission", - + "By Vendor View": "By Vendor View", + "PERMISSIONS": "PERMISSIONS", + "Contributor": "Contributor", + "Active Items": "Active Items", + "Archived Items": "Archived Items", + "VendorSoftwareProduct": "VSP", "VendorSoftwareProduct/category": "Category", "VendorSoftwareProduct/description": "Description", diff --git a/openecomp-ui/src/nfvo-utils/objectPropsToUrlString.js b/openecomp-ui/src/nfvo-utils/objectPropsToUrlString.js new file mode 100644 index 0000000000..6c18bb6111 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/objectPropsToUrlString.js @@ -0,0 +1,23 @@ +export default function objectPropsToUrlString(data) { + let str = ''; + Object.keys(data).map(key => { + if (typeof data[key] === 'object') { + let obj = data[key]; + let arr = []; + + Object.keys(obj).map(prop => { + if (obj[prop]) { + arr.push(encodeURIComponent(prop)); + } + }); + if (arr.length) { + str += `&${encodeURIComponent(key)}=${arr.join(',')}`; + } + } else if (data[key]) { + str += `&${encodeURIComponent(key)}=${encodeURIComponent( + data[key] + )}`; + } + }); + return str; +} diff --git a/openecomp-ui/src/sdc-app/AppStore.js b/openecomp-ui/src/sdc-app/AppStore.js index bca750a930..5cab6ae561 100644 --- a/openecomp-ui/src/sdc-app/AppStore.js +++ b/openecomp-ui/src/sdc-app/AppStore.js @@ -16,17 +16,20 @@ import { createStore, applyMiddleware, compose } from 'redux'; import Reducers from './Reducers.js'; +import filterUpdater from 'sdc-app/onboarding/onboard/filter/FilterMiddleware.js'; + const thunk = store => next => action => typeof action === 'function' ? action(store.dispatch, store.getState) : next(action); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; + export const storeCreator = initialState => createStore( Reducers, initialState, - composeEnhancers(applyMiddleware(thunk)) + composeEnhancers(applyMiddleware(thunk, filterUpdater)) ); const store = storeCreator(); diff --git a/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js b/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js index 99ecae9887..ae5c2707b6 100644 --- a/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js +++ b/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js @@ -1,36 +1,32 @@ -/*! - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +/* + * Copyright © 2016-2018 European Support Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing - * permissions and limitations under the License. + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; import Configuration from 'sdc-app/config/Configuration.js'; import { permissionTypes } from 'sdc-app/onboarding/permissions/PermissionsConstants.js'; import { actionsEnum as VersionControllerActionsEnum } from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; import { actionTypes as onboardingActionTypes } from 'sdc-app/onboarding/OnboardingConstants.js'; -import restToggle from 'sdc-app/features/restToggle.js'; +import { restToggle } from 'sdc-app/features/featureToggleUtils.js'; import { featureToggleNames } from 'sdc-app/features/FeaturesConstants.js'; +import objectPropsToUrlString from 'nfvo-utils/objectPropsToUrlString.js'; + export const archiveActions = { ARCHIVE: 'ARCHIVE', RESTORE: 'RESTORE' }; -export const itemStatus = { - ARCHIVED: 'ARCHIVED', - DRAFT: 'Draft', - CERTIFIED: 'Certified' -}; - function baseUrl() { const restPrefix = Configuration.get('restPrefix'); return `${restPrefix}/v1.0/items`; @@ -118,6 +114,15 @@ const ItemsHelper = { return RestAPIUtil.put(`${baseUrl()}/${itemId}/actions`, { action: archiveActions.RESTORE }); + }, + + fetchItems(filterData) { + const str = objectPropsToUrlString(filterData); + return restToggle({ + restFunction: () => RestAPIUtil.fetch(`${baseUrl()}?${str}`), + featureName: featureToggleNames.FILTER, + mockResult: { results: [] } + }); } }; diff --git a/openecomp-ui/src/sdc-app/common/helpers/ItemsHelperConstants.js b/openecomp-ui/src/sdc-app/common/helpers/ItemsHelperConstants.js new file mode 100644 index 0000000000..ebbbfded3b --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/helpers/ItemsHelperConstants.js @@ -0,0 +1,29 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const itemStatus = { + ACTIVE: 'ACTIVE', + ARCHIVED: 'ARCHIVED' +}; + +export const versionStatus = { + DRAFT: 'Draft', + CERTIFIED: 'Certified' +}; + +export const itemType = { + VSP: 'vsp', + VLM: 'vlm' +}; diff --git a/openecomp-ui/src/sdc-app/common/helpers/UniqueTypesHelper.js b/openecomp-ui/src/sdc-app/common/helpers/UniqueTypesHelper.js new file mode 100644 index 0000000000..de84f91bae --- /dev/null +++ b/openecomp-ui/src/sdc-app/common/helpers/UniqueTypesHelper.js @@ -0,0 +1,69 @@ +/*! + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; +import { featureToggleNames } from 'sdc-app/features/FeaturesConstants.js'; +import { restToggle } from 'sdc-app/features/featureToggleUtils.js'; + +const itemTypesMapper = { + vsp: 'VspName', + vlm: 'VlmName' +}; + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/unique-types/`; +} + +function uniqueValue(type, value) { + return restToggle({ + restFunction: () => + RestAPIUtil.fetch(`${baseUrl()}${type}/values/${value}`), + featureName: featureToggleNames.FILTER, + mockResult: { occupied: false } + }); +} + +export default { + async isNameUnique( + dispatch, + { value, name, formName, errorText, itemType } + ) { + const { occupied } = await uniqueValue( + itemTypesMapper[itemType], + value + ); + const validation = occupied + ? { + isValid: false, + errorText + } + : { isValid: true, errorText: '' }; + + let deltaData = {}; + deltaData[name] = value; + let customValidations = {}; + customValidations[name] = () => validation; + + ValidationHelper.dataChanged(dispatch, { + deltaData, + formName, + customValidations + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/features/featureToggle.js b/openecomp-ui/src/sdc-app/features/featureToggle.js index d33c76e523..861db6ae9c 100644 --- a/openecomp-ui/src/sdc-app/features/featureToggle.js +++ b/openecomp-ui/src/sdc-app/features/featureToggle.js @@ -38,12 +38,14 @@ import { connect } from 'react-redux'; export const FeatureComponent = props => { const { features = [], featureName, InnerComponent, ...otherProps } = props; - const AComp = InnerComponent.AComp ? InnerComponent.AComp : InnerComponent; + const OnComp = InnerComponent.OnComp + ? InnerComponent.OnComp + : InnerComponent; return !!features.find(el => el.name === featureName && el.active) ? ( - <AComp {...otherProps} /> - ) : InnerComponent.BComp ? ( - <InnerComponent.BComp {...otherProps} /> + <OnComp {...otherProps} /> + ) : InnerComponent.OffComp ? ( + <InnerComponent.OffComp {...otherProps} /> ) : null; }; diff --git a/openecomp-ui/src/sdc-app/features/restToggle.js b/openecomp-ui/src/sdc-app/features/featureToggleUtils.js index 505dace4e7..1263336fd3 100644 --- a/openecomp-ui/src/sdc-app/features/restToggle.js +++ b/openecomp-ui/src/sdc-app/features/featureToggleUtils.js @@ -16,9 +16,16 @@ import store from 'sdc-app/AppStore.js'; -export default ({ featureName, restFunction, mockResult }) => { +export const restToggle = ({ featureName, restFunction, mockResult }) => { const { features } = store.getState(); return !!features.find(el => el.name === featureName && el.active) ? restFunction() : Promise.resolve(mockResult); }; + +export const functionToggle = (featureName, { onFunction, offFunction }) => { + const { features } = store.getState(); + return !!features.find(el => el.name === featureName && el.active) + ? onFunction() + : offFunction(); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js index 2fccfcbd2a..b8ce714bce 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -45,6 +45,8 @@ import SoftwareProductComponentsImageActionHelper from './softwareProduct/compon import licenseModelOverviewActionHelper from 'sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js'; import { tabsMapping as attachmentsTabsMapping } from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js'; import SoftwareProductAttachmentsActionHelper from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js'; +import { actionTypes as filterActionTypes } from './onboard/filter/FilterConstants.js'; +import FeaturesActionHelper from 'sdc-app/features/FeaturesActionHelper.js'; function setCurrentScreen(dispatch, screen, props = {}) { dispatch({ @@ -74,11 +76,16 @@ const OnboardingActionHelper = { SoftwareProductActionHelper.fetchArchivedSoftwareProductList(dispatch); }, - navigateToOnboardingCatalog(dispatch) { + async navigateToOnboardingCatalog(dispatch) { + await FeaturesActionHelper.getFeaturesList(dispatch); UsersActionHelper.fetchUsersList(dispatch); this.loadItemsLists(dispatch); OnboardActionHelper.resetOnboardStore(dispatch); setCurrentScreen(dispatch, enums.SCREEN.ONBOARDING_CATALOG); + dispatch({ + type: filterActionTypes.FILTER_DATA_CHANGED, + deltaData: {} + }); }, autoSaveBeforeNavigate( @@ -207,6 +214,7 @@ const OnboardingActionHelper = { { softwareProductId, version, status } ) { SoftwareProductComponentsActionHelper.clearComponentsStore(dispatch); + LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, { softwareProductId, version @@ -559,7 +567,7 @@ const OnboardingActionHelper = { ); }, - navigateToVersionsPage( + async navigateToVersionsPage( dispatch, { itemType, itemId, itemName, additionalProps, users } ) { @@ -568,19 +576,19 @@ const OnboardingActionHelper = { allUsers: users }); VersionsPageActionHelper.selectNone(dispatch); - VersionsPageActionHelper.fetchVersions(dispatch, { + await VersionsPageActionHelper.fetchVersions(dispatch, { itemType, itemId - }).then(() => { - ItemsHelper.fetchItem(itemId).then(result => { - setCurrentScreen(dispatch, enums.SCREEN.VERSIONS_PAGE, { - status: result.status, - itemType, - itemId, - itemName, - additionalProps - }); - }); + }); + const items = await ItemsHelper.fetchItem(itemId); + setCurrentScreen(dispatch, enums.SCREEN.VERSIONS_PAGE, { + status: items.status, + itemType, + itemId, + itemName, + vendorName: items.properties.vendorName, + vendorId: items.properties.vendorId, + additionalProps }); }, diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx index f462dd790b..245dd2b55c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -21,8 +21,6 @@ import ReactDOM from 'react-dom'; import isEqual from 'lodash/isEqual.js'; -import lodashUnionBy from 'lodash/unionBy.js'; - import i18n from 'nfvo-utils/i18n/i18n.js'; import Application from 'sdc-app/Application.jsx'; import store from 'sdc-app/AppStore.js'; @@ -356,10 +354,6 @@ export default class OnboardingPunchOut { handleStoreChange() { let { currentScreen, - licenseModelList, - finalizedLicenseModelList, - softwareProductList, - finalizedSoftwareProductList, versionsPage: { versionsList: { itemType, itemId } }, softwareProduct: { softwareProductEditor: { @@ -367,26 +361,17 @@ export default class OnboardingPunchOut { }, softwareProductComponents: { componentsList } }, - archivedLicenseModelList, - archivedSoftwareProductList + licenseModel: { + licenseModelEditor: { data: currentLicenseModel = {} } + } } = store.getState(); - const wholeSoftwareProductList = lodashUnionBy( - softwareProductList, - [...finalizedSoftwareProductList, ...archivedSoftwareProductList], - 'id' - ); - const wholeLicenseModelList = lodashUnionBy( - licenseModelList, - [...finalizedLicenseModelList, ...archivedLicenseModelList], - 'id' - ); + let breadcrumbsData = { itemType, itemId, currentScreen, - wholeLicenseModelList, - wholeSoftwareProductList, currentSoftwareProduct, + currentLicenseModel, componentsList }; @@ -415,8 +400,7 @@ export default class OnboardingPunchOut { itemType, itemId, currentSoftwareProduct, - wholeLicenseModelList, - wholeSoftwareProductList, + currentLicenseModel, componentsList }) { let { @@ -435,12 +419,12 @@ export default class OnboardingPunchOut { ? [ { selectedKey: itemId, - menuItems: wholeLicenseModelList.map( - ({ id, name }) => ({ - key: id, - displayText: name - }) - ) + menuItems: [ + { + key: itemId, + displayText: props.itemName + } + ] } ] : [ @@ -448,12 +432,12 @@ export default class OnboardingPunchOut { selectedKey: props.additionalProps.licenseModelId || currentSoftwareProduct.vendorId, - menuItems: wholeLicenseModelList.map( - ({ id, name }) => ({ - key: id, - displayText: name - }) - ) + menuItems: [ + { + key: props.vendorId, + displayText: props.vendorName + } + ] }, { selectedKey: @@ -472,17 +456,12 @@ export default class OnboardingPunchOut { }, { selectedKey: itemId, - menuItems: wholeSoftwareProductList - .filter( - ({ id, vendorId }) => - vendorId === - currentSoftwareProduct.vendorId || - id === itemId - ) - .map(({ id, name }) => ({ - key: id, - displayText: name - })) + menuItems: [ + { + key: itemId, + displayText: props.itemName + } + ] } ]; return [ @@ -519,13 +498,13 @@ export default class OnboardingPunchOut { }; return [ { - selectedKey: props.licenseModelId, - menuItems: wholeLicenseModelList.map( - ({ id, name }) => ({ - key: id, - displayText: name - }) - ) + selectedKey: currentLicenseModel.id, + menuItems: [ + { + key: currentLicenseModel.id, + displayText: currentLicenseModel.vendorName + } + ] }, { selectedKey: enums.BREADCRUMS.LICENSE_MODEL, @@ -533,19 +512,7 @@ export default class OnboardingPunchOut { { key: enums.BREADCRUMS.LICENSE_MODEL, displayText: i18n('License Model') - }, - ...(wholeSoftwareProductList.findIndex( - ({ vendorId }) => - vendorId === props.licenseModelId - ) === -1 - ? [] - : [ - { - key: - enums.BREADCRUMS.SOFTWARE_PRODUCT, - displayText: i18n('Software Products') - } - ]) + } ] }, { @@ -636,16 +603,16 @@ export default class OnboardingPunchOut { [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING }; - let licenseModelId = currentSoftwareProduct.vendorId; + let returnedBreadcrumb = [ { - selectedKey: licenseModelId, - menuItems: wholeLicenseModelList.map( - ({ id, name }) => ({ - key: id, - displayText: name - }) - ) + selectedKey: currentSoftwareProduct.vendorId, + menuItems: [ + { + key: currentSoftwareProduct.vendorId, + displayText: currentSoftwareProduct.vendorName + } + ] }, { selectedKey: enums.BREADCRUMS.SOFTWARE_PRODUCT, @@ -661,17 +628,13 @@ export default class OnboardingPunchOut { ] }, { - selectedKey: props.softwareProductId, - menuItems: wholeSoftwareProductList - .filter( - ({ vendorId, id }) => - vendorId === licenseModelId || - id === props.softwareProductId - ) - .map(({ id, name }) => ({ - key: id, - displayText: name - })) + selectedKey: currentSoftwareProduct.id, + menuItems: [ + { + key: currentSoftwareProduct.id, + displayText: currentSoftwareProduct.name + } + ] }, .../*screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] :*/ [ { diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js index 3b526a67f6..09f4ffb930 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js @@ -28,7 +28,7 @@ import usersReducer from './users/UsersReducers.js'; import mergeEditorReducer from 'sdc-app/common/merge/MergeEditorReducer.js'; import revisionsReducer from './revisions/RevisionsReducer.js'; import featuresReducer from 'sdc-app/features/FeaturesReducer.js'; - +import itemsReducer from 'sdc-app/onboarding/onboard/filter/ItemsReducer.js'; export default { currentScreen: currentScreenReducer, licenseModel: licenseModelReducer, @@ -44,5 +44,6 @@ export default { users: usersReducer, versionsPage: versionsPageReducer, revisions: revisionsReducer, - features: featuresReducer + features: featuresReducer, + filteredItems: itemsReducer }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js index cfff9f1fcd..be33af7910 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js @@ -29,8 +29,11 @@ import { modalContentMapper } from 'sdc-app/common/modal/ModalContentMapper.js'; import { CommitModalType } from 'nfvo-components/panel/versionController/components/CommitCommentModal.jsx'; import versionPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js'; import { itemTypes } from 'sdc-app/onboarding/versionsPage/VersionsPageConstants.js'; -import { catalogItemStatuses } from 'sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js'; import { actionsEnum as VersionControllerActionsEnum } from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import { + itemStatus, + versionStatus +} from 'sdc-app/common/helpers/ItemsHelperConstants.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -39,19 +42,17 @@ function baseUrl() { function fetchLicenseModels() { return RestAPIUtil.fetch( - `${baseUrl()}?versionFilter=${catalogItemStatuses.DRAFT}` + `${baseUrl()}?versionFilter=${versionStatus.DRAFT}` ); } function fetchFinalizedLicenseModels() { return RestAPIUtil.fetch( - `${baseUrl()}?versionFilter=${catalogItemStatuses.CERTIFIED}` + `${baseUrl()}?versionFilter=${versionStatus.CERTIFIED}` ); } function fetchArchivedLicenseModels() { - return RestAPIUtil.fetch( - `${baseUrl()}?Status=${catalogItemStatuses.ARCHIVED}` - ); + return RestAPIUtil.fetch(`${baseUrl()}?Status=${itemStatus.ARCHIVED}`); } function fetchLicenseModelById(licenseModelId, version) { const { id: versionId } = version; @@ -206,9 +207,8 @@ const LicenseModelActionHelper = { version }).then(({ inMerge, isDirty, updatedVersion }) => { if ( - (updatedVersion.status === catalogItemStatuses.CERTIFIED || - updatedVersion.archivedStatus === - catalogItemStatuses.ARCHIVED) && + (updatedVersion.status === versionStatus.CERTIFIED || + updatedVersion.archivedStatus === versionStatus.ARCHIVED) && (action === VersionControllerActionsEnum.COMMIT || action === VersionControllerActionsEnum.SYNC) ) { @@ -217,8 +217,7 @@ const LicenseModelActionHelper = { itemId: licenseModelId }); const msg = - updatedVersion.archivedStatus === - catalogItemStatuses.ARCHIVED + updatedVersion.archivedStatus === versionStatus.ARCHIVED ? i18n('Item was Archived') : i18n('Item version already Certified'); dispatch({ 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 4bbab865fa..c6a0702a57 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js @@ -14,8 +14,12 @@ * limitations under the License. */ import { connect } from 'react-redux'; +import featureToggle from 'sdc-app/features/featureToggle.js'; +import { featureToggleNames } from 'sdc-app/features/FeaturesConstants.js'; import LicenseModelCreationActionHelper from './LicenseModelCreationActionHelper.js'; import LicenseModelCreationView from './LicenseModelCreationView.jsx'; +import LicenseModelCreationViewWithFilter from './LicenseModelCreationViewWithFilter.jsx'; + import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; import VersionsPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js'; @@ -23,6 +27,16 @@ import { itemTypes as versionItemTypes } from 'sdc-app/onboarding/versionsPage/V import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js'; import { enums, screenTypes } from 'sdc-app/onboarding/OnboardingConstants.js'; import PermissionsActionHelper from 'sdc-app/onboarding/permissions/PermissionsActionHelper.js'; +import UniqueTypesHelper from 'sdc-app/common/helpers/UniqueTypesHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import { itemType } from 'sdc-app/common/helpers/ItemsHelperConstants.js'; + +const ToggledLicenseModelCreationView = featureToggle( + featureToggleNames.FILTER +)({ + OnComp: LicenseModelCreationViewWithFilter, + OffComp: LicenseModelCreationView +}); export const mapStateToProps = ({ users: { usersList }, @@ -87,10 +101,22 @@ export const mapActionsToProps = dispatch => { }); }, onValidateForm: formName => - ValidationHelper.validateForm(dispatch, formName) + ValidationHelper.validateForm(dispatch, formName), + isNameUnique: (value, name, formName) => + UniqueTypesHelper.isNameUnique(dispatch, { + value, + name, + formName, + errorText: `${i18n( + 'License model by the name' + )} ${value} ${i18n('already exists')}. ${i18n( + 'License model name must be unique' + )}`, + itemType: itemType.VLM + }) }; }; export default connect(mapStateToProps, mapActionsToProps)( - LicenseModelCreationView + ToggledLicenseModelCreationView ); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js index 7137230cc7..5922a47822 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js @@ -40,7 +40,8 @@ export default (state = {}, action) => { errorText: '', validations: [ { type: 'required', data: true }, - { type: 'maxLength', data: 25 } + { type: 'maxLength', data: 25 }, + { type: 'validateName', data: true } ] } } diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationViewWithFilter.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationViewWithFilter.jsx new file mode 100644 index 0000000000..8c5d966938 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationViewWithFilter.jsx @@ -0,0 +1,116 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 PropTypes from 'prop-types'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import { LICENSE_MODEL_CREATION_FORM_NAME } from './LicenseModelCreationConstants.js'; + +const LicenseModelPropType = PropTypes.shape({ + id: PropTypes.string, + vendorName: PropTypes.string, + description: PropTypes.string +}); + +class LicenseModelCreationView extends React.Component { + static propTypes = { + data: LicenseModelPropType, + VLMNames: PropTypes.object, + usersList: PropTypes.array, + onDataChanged: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + onValidateForm: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired + }; + + render() { + let { data = {}, onDataChanged, genericFieldInfo } = this.props; + let { vendorName, description } = data; + return ( + <div> + {genericFieldInfo && ( + <Form + ref="validationForm" + hasButtons={true} + onSubmit={() => this.submit()} + submitButtonText={i18n('Create')} + onReset={() => this.props.onCancel()} + labledButtons={true} + isValid={this.props.isFormValid} + formReady={this.props.formReady} + onValidateForm={() => this.validate()}> + <Input + value={vendorName} + label={i18n('Vendor Name')} + data-test-id="vendor-name" + onChange={vendorName => + onDataChanged( + { vendorName }, + LICENSE_MODEL_CREATION_FORM_NAME + ) + } + onBlur={e => + this.validateIsNameUnique(e.target.value) + } + isValid={genericFieldInfo.vendorName.isValid} + errorText={genericFieldInfo.vendorName.errorText} + type="text" + isRequired={true} + className="field-section" + /> + <Input + isRequired={true} + value={description} + label={i18n('Description')} + data-test-id="vendor-description" + overlayPos="bottom" + onChange={description => + onDataChanged( + { description }, + LICENSE_MODEL_CREATION_FORM_NAME + ) + } + isValid={genericFieldInfo.description.isValid} + errorText={genericFieldInfo.description.errorText} + type="textarea" + className="field-section" + /> + </Form> + )} + </div> + ); + } + + submit() { + const { data: licenseModel, usersList } = this.props; + this.props.onSubmit(licenseModel, usersList); + } + + validateIsNameUnique(value) { + this.props.isNameUnique( + value, + 'vendorName', + LICENSE_MODEL_CREATION_FORM_NAME + ); + } + + validate() { + this.props.onValidateForm(LICENSE_MODEL_CREATION_FORM_NAME); + } +} + +export default LicenseModelCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js index d01b9d0d04..c0de0eeb79 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.stories.js @@ -2,11 +2,9 @@ import React from 'react'; import { storiesOf, action } from '@kadira/storybook'; import { select, withKnobs } from '@kadira/storybook-addon-knobs'; import CatalogItemDetails from './CatalogItemDetails.jsx'; -import { - catalogItemTypes, - catalogItemStatuses -} from './onboardingCatalog/OnboardingCatalogConstants.js'; +import { catalogItemTypes } from './onboardingCatalog/OnboardingCatalogConstants.js'; import { FinalizedLicenseModelFactory } from 'test-utils/factories/licenseModel/LicenseModelFactories.js'; +import { versionStatus } from 'sdc-app/common/helpers/ItemsHelperConstants.js'; const stories = storiesOf('CatalogTiles', module); stories.addDecorator(withKnobs); @@ -22,9 +20,9 @@ function selectType() { let vlm = { ...FinalizedLicenseModelFactory.build({ name: 'Test-VLM' }), - itemStatus: catalogItemStatuses.DRAFT + itemStatus: versionStatus.DRAFT }; -let certifiedVlm = { ...vlm, itemStatus: catalogItemStatuses.CERTIFIED }; +let certifiedVlm = { ...vlm, itemStatus: versionStatus.CERTIFIED }; stories.add('preview', () => ( <div className="catalog-view"> diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx index 771c0eb6c1..b535595355 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/DetailsCatalogView.jsx @@ -13,6 +13,7 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ +import isEqual from 'lodash/isEqual.js'; import React from 'react'; import PropTypes from 'prop-types'; import { catalogItemTypes } from './onboardingCatalog/OnboardingCatalogConstants.js'; @@ -20,6 +21,28 @@ import { filterCatalogItemsByType } from './onboardingCatalog/OnboardingCatalogU import CatalogList from './CatalogList.jsx'; import CatalogItemDetails from './CatalogItemDetails.jsx'; +function renderCatalogItems({ + items, + type, + filter, + onSelect, + onMigrate, + users +}) { + const filteredItems = items.length + ? filterCatalogItemsByType({ items, filter }) + : []; + return filteredItems.map(item => ( + <CatalogItemDetails + key={item.id} + catalogItemData={item} + catalogItemTypeClass={type} + onMigrate={onMigrate} + onSelect={() => onSelect(item, users)} + /> + )); +} + class DetailsCatalogView extends React.Component { static propTypes = { VLMList: PropTypes.array, @@ -31,18 +54,14 @@ class DetailsCatalogView extends React.Component { filter: PropTypes.string.isRequired }; - renderCatalogItems({ items, type, filter, onSelect, onMigrate, users }) { - return filterCatalogItemsByType({ items, filter }).map(item => ( - <CatalogItemDetails - key={item.id} - catalogItemData={item} - catalogItemTypeClass={type} - onMigrate={onMigrate} - onSelect={() => onSelect(item, users)} - /> - )); + shouldComponentUpdate(nextProps) { + const shouldUpdate = + isEqual(nextProps.VLMList, this.props.VLMList) && + isEqual(nextProps.VSPList, this.props.VSPList) && + isEqual(nextProps.users, this.props.users) && + isEqual(nextProps.filter, this.props.filter); + return !shouldUpdate; } - render() { let { VLMList, @@ -57,7 +76,7 @@ class DetailsCatalogView extends React.Component { } = this.props; return ( <CatalogList onAddVLM={onAddVLM} onAddVSP={onAddVSP}> - {this.renderCatalogItems({ + {renderCatalogItems({ items: VLMList, type: catalogItemTypes.LICENSE_MODEL, filter, @@ -65,7 +84,7 @@ class DetailsCatalogView extends React.Component { onMigrate, users })} - {this.renderCatalogItems({ + {renderCatalogItems({ items: VSPList, type: catalogItemTypes.SOFTWARE_PRODUCT, filter, diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js b/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js index a1e0018114..ea70f9c0b8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/Onboard.js @@ -22,7 +22,7 @@ import LicenseModelCreationActionHelper from '../licenseModel/creation/LicenseMo import SoftwareProductCreationActionHelper from '../softwareProduct/creation/SoftwareProductCreationActionHelper.js'; import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; import { tabsMapping } from './onboardingCatalog/OnboardingCatalogConstants.js'; -import { itemsType } from './filter/FilterConstants.js'; +import { itemStatus } from 'sdc-app/common/helpers/ItemsHelperConstants.js'; export const mapStateToProps = ({ onboard: { onboardingCatalog, activeTab, searchValue, filter }, @@ -32,7 +32,8 @@ export const mapStateToProps = ({ archivedSoftwareProductList, finalizedLicenseModelList, softwareProductList, - finalizedSoftwareProductList + finalizedSoftwareProductList, + filteredItems }) => { const fullSoftwareProducts = softwareProductList .filter( @@ -50,6 +51,23 @@ export const mapStateToProps = ({ return accum; }; + const reduceFilteredLicenseModelList = (accum, vlm) => { + let currentSoftwareProductList = sortByStringProperty( + filteredItems.vspList.filter(vsp => vsp.vendorId === vlm.id), + 'name' + ); + accum.push({ ...vlm, softwareProductList: currentSoftwareProductList }); + return accum; + }; + + const updatedFilteredItems = { + vspList: [...filteredItems.vspList], + vlmList: sortByStringProperty( + filteredItems.vlmList.reduce(reduceFilteredLicenseModelList, []), + 'name' + ) + }; + licenseModelList = sortByStringProperty( licenseModelList.reduce(reduceLicenseModelList, []), 'name' @@ -72,7 +90,7 @@ export const mapStateToProps = ({ } = onboardingCatalog; if (filter.byVendorView) { catalogActiveTab = tabsMapping.BY_VENDOR; - } else if (filter.itemsType && filter.itemsType === itemsType.ARCHIVED) { + } else if (filter.itemStatus && filter.itemStatus === itemStatus.ARCHIVED) { catalogActiveTab = tabsMapping.ARCHIVE; } @@ -89,7 +107,8 @@ export const mapStateToProps = ({ searchValue, vspOverlay, selectedVendor, - users: users.usersList + users: users.usersList, + filteredItems: updatedFilteredItems }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js index 87ec2d148e..2826e324b3 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardActionHelper.js @@ -1,25 +1,30 @@ -/*! - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +/* + * Copyright © 2016-2018 European Support Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing - * permissions and limitations under the License. + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ import { tabsMapping, actionTypes } from './OnboardConstants.js'; import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js'; import { enums, screenTypes } from 'sdc-app/onboarding/OnboardingConstants.js'; import VersionsPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js'; -import { catalogItemStatuses } from 'sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js'; + import { itemTypes } from 'sdc-app/onboarding/versionsPage/VersionsPageConstants.js'; import PermissionsActionHelper from 'sdc-app/onboarding/permissions/PermissionsActionHelper.js'; +import { actionTypes as filterActionTypes } from './filter/FilterConstants.js'; +import { + versionStatus, + itemStatus +} from 'sdc-app/common/helpers/ItemsHelperConstants.js'; const OnboardActionHelper = { resetOnboardStore(dispatch) { @@ -33,6 +38,18 @@ const OnboardActionHelper = { type: actionTypes.CHANGE_ACTIVE_ONBOARD_TAB, activeTab }); + dispatch({ + type: filterActionTypes.FILTER_DATA_CHANGED, + deltaData: + activeTab === tabsMapping.WORKSPACE + ? { + versionStatus: versionStatus.DRAFT, + itemStatus: itemStatus.ACTIVE + } + : { + versionStatus: versionStatus.CERTIFIED + } + }); }, changeSearchValue(dispatch, searchValue) { dispatch({ @@ -54,7 +71,7 @@ const OnboardActionHelper = { itemType: itemTypes.LICENSE_MODEL }).then(({ results }) => { results = results.filter( - version => version.status === catalogItemStatuses.DRAFT + version => version.status === versionStatus.DRAFT ); if (results.length !== 1) { ScreensHelper.loadScreen(dispatch, { @@ -104,7 +121,7 @@ const OnboardActionHelper = { itemType: itemTypes.SOFTWARE_PRODUCT }).then(({ results }) => { results = results.filter( - version => version.status === catalogItemStatuses.DRAFT + version => version.status === versionStatus.DRAFT ); if (results.length !== 1) { ScreensHelper.loadScreen(dispatch, { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx index dcaeaa787d..0fc64b328c 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/OnboardView.jsx @@ -16,7 +16,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import OnboardingCatalogView from './onboardingCatalog/OnboardingCatalogView.jsx'; +import OnboardingCatalogViewWithFilter from './onboardingCatalog/OnboardingCatalogViewWithFilter.jsx'; import WorkspaceView from './workspace/WorkspaceView.jsx'; +import WorkspaceViewWithFilter from './workspace/WorkspaceViewWithFilter.jsx'; import { tabsMapping } from './OnboardConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import classnames from 'classnames'; @@ -25,7 +27,8 @@ import objectValues from 'lodash/values.js'; import { catalogItemTypes } from './onboardingCatalog/OnboardingCatalogConstants.js'; import NotificationsView from 'sdc-app/onboarding/userNotifications/NotificationsView.jsx'; import Filter from 'sdc-app/onboarding/onboard/filter/Filter.jsx'; - +import featureToggle from 'sdc-app/features/featureToggle.js'; +import { featureToggleNames } from 'sdc-app/features/FeaturesConstants.js'; const OnboardHeaderTabs = ({ onTabClick, activeTab }) => ( <div className="onboard-header-tabs"> <div @@ -47,6 +50,16 @@ const OnboardHeaderTabs = ({ onTabClick, activeTab }) => ( </div> ); +const ToggledOnboardingCatalogView = featureToggle(featureToggleNames.FILTER)({ + OnComp: OnboardingCatalogViewWithFilter, + OffComp: OnboardingCatalogView +}); + +const ToggledWorkspaceView = featureToggle(featureToggleNames.FILTER)({ + OnComp: WorkspaceViewWithFilter, + OffComp: WorkspaceView +}); + const OnboardHeader = ({ onSearch, activeTab, onTabClick, searchValue }) => ( <div className="onboard-header"> <OnboardHeaderTabs activeTab={activeTab} onTabClick={onTabClick} /> @@ -85,11 +98,11 @@ class OnboardView extends React.Component { renderViewByTab(activeTab) { switch (activeTab) { case tabsMapping.WORKSPACE: - return <WorkspaceView {...this.props} />; + return <ToggledWorkspaceView {...this.props} />; case tabsMapping.CATALOG: - return <OnboardingCatalogView {...this.props} />; + return <ToggledOnboardingCatalogView {...this.props} />; default: - return <WorkspaceView {...this.props} />; + return <ToggledWorkspaceView {...this.props} />; } } diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/Filter.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/Filter.jsx index c80232de0a..a00357c7b9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/Filter.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/Filter.jsx @@ -17,14 +17,19 @@ import { connect } from 'react-redux'; import React from 'react'; import PropTypes from 'prop-types'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Input from 'nfvo-components/input/validation/Input.jsx'; -import Accordion from 'nfvo-components/accordion/Accordion.jsx'; -import { actionTypes } from './FilterConstants.js'; import featureToggle from 'sdc-app/features/featureToggle.js'; import { featureToggleNames } from 'sdc-app/features/FeaturesConstants.js'; import { tabsMapping as onboardTabsMapping } from '../OnboardConstants.js'; -import { itemsType as itemsTypeConstants } from './FilterConstants.js'; +import { actionTypes } from './FilterConstants.js'; + +import Panel from 'sdc-ui/lib/react/Panel.js'; +import { + ItemStatus, + ByVendorView, + EntityType, + Permissions, + OnboardingProcedure +} from './FilterComponents.jsx'; const mapStateToProps = ({ onboard: { filter, activeTab } }) => { return { @@ -35,145 +40,39 @@ const mapStateToProps = ({ onboard: { filter, activeTab } }) => { const mapActionsToProps = dispatch => { return { - onDataChanged: deltaData => + onDataChanged: deltaData => { dispatch({ type: actionTypes.FILTER_DATA_CHANGED, deltaData - }) + }); + } }; }; -const Filter = ({ - onDataChanged, - data: { - entityTypeVsp, - entityTypeVlm, - roleOwner, - roleContributor, - roleViewer, - procedureNetwork, - procedureManual, - recentlyUpdated, - byVendorView, - itemsType - }, - activeTab -}) => ( - <div className="catalog-filter"> - {activeTab === onboardTabsMapping.CATALOG && ( - <Input - type="select" - className="catalog-filter-items-type" - data-test-id="catalog-filter-items-type" - disabled={byVendorView} - value={itemsType} - onChange={e => onDataChanged({ itemsType: e.target.value })}> - <option - key={itemsTypeConstants.ACTIVE} - value={itemsTypeConstants.ACTIVE}> - Active Items - </option> - <option - key={itemsTypeConstants.ARCHIVED} - value={itemsTypeConstants.ARCHIVED}> - Archived Items - </option> - </Input> - )} - {activeTab === onboardTabsMapping.CATALOG && ( - <Input - label={i18n('By Vendor View')} - type="checkbox" - disabled={itemsType === itemsTypeConstants.ARCHIVED} - checked={byVendorView} - onChange={byVendorView => onDataChanged({ byVendorView })} - data-test-id="filter-by-vendor-view" - value="" - /> - )} - <Input - label={i18n('Recently Updated')} - type="checkbox" - checked={recentlyUpdated} - onChange={recentlyUpdated => onDataChanged({ recentlyUpdated })} - data-test-id="filter-recently-updated" - value="" - /> - - <Accordion title={i18n('ENTITY TYPE')}> - <Input - label={i18n('VSP')} - type="checkbox" - checked={entityTypeVsp} - onChange={entityTypeVsp => onDataChanged({ entityTypeVsp })} - data-test-id="filter-type-vsp" - value="" - /> - <Input - label={i18n('VLM')} - type="checkbox" - checked={entityTypeVlm} - onChange={entityTypeVlm => onDataChanged({ entityTypeVlm })} - data-test-id="filter-type-vlm" - value="" - /> - </Accordion> - - <Accordion title={i18n('ROLE')}> - <Input - label={i18n('Owner')} - type="checkbox" - checked={roleOwner} - onChange={roleOwner => onDataChanged({ roleOwner })} - data-test-id="filter-role-owner" - value="" - /> - <Input - label={i18n('Contributer')} - type="checkbox" - checked={roleContributor} - onChange={roleContributor => onDataChanged({ roleContributor })} - data-test-id="filter-role-contributor" - value="" - /> - <Input - label={i18n('Viewer')} - type="checkbox" - checked={roleViewer} - onChange={roleViewer => onDataChanged({ roleViewer })} - data-test-id="filter-role-viewr" - value="" - /> - </Accordion> - - <Accordion title={i18n('ONBOARDING PROCEDURE')}> - <Input - label={i18n('Network Package')} - type="checkbox" - checked={procedureNetwork} - onChange={procedureNetwork => - onDataChanged({ procedureNetwork }) - } - data-test-id="filter-procedure-network" - value="" - /> - <Input - label={i18n('Manual')} - type="checkbox" - checked={procedureManual} - onChange={procedureManual => onDataChanged({ procedureManual })} - data-test-id="filter-procedure-manual" - value="" - /> - </Accordion> - </div> -); +const Filter = ({ onDataChanged, data, activeTab }) => { + return ( + <Panel className="catalog-filter"> + <ItemStatus data={data} onDataChanged={onDataChanged} /> + <EntityType data={data} onDataChanged={onDataChanged} /> + <Permissions data={data} onDataChanged={onDataChanged} /> + <OnboardingProcedure data={data} onDataChanged={onDataChanged} /> + {activeTab === onboardTabsMapping.CATALOG && ( + <ByVendorView data={data} onDataChanged={onDataChanged} /> + )} + </Panel> + ); +}; Filter.PropTypes = { onDataChanged: PropTypes.func, - data: PropTypes.object + data: PropTypes.object, + activeTab: PropTypes.number }; export default featureToggle(featureToggleNames.FILTER)( connect(mapStateToProps, mapActionsToProps)(Filter) ); + +export const ConnectedFilter = connect(mapStateToProps, mapActionsToProps)( + Filter +); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterActionHelper.js new file mode 100644 index 0000000000..f8155df49d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterActionHelper.js @@ -0,0 +1,60 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 { default as ItemsHelper } from 'sdc-app/common/helpers/ItemsHelper.js'; +import { + itemType, + versionStatus +} from 'sdc-app/common/helpers/ItemsHelperConstants.js'; +import { actionTypes } from './FilterConstants.js'; + +const FilterActionHelper = { + async updateFilteredItems(dispatch, filter) { + let permission = { ...filter.permission }; + if ( + filter.versionStatus === versionStatus.DRAFT && + !permission.Owner && + !permission.Contributor + ) { + permission.Owner = true; + permission.Contributor = true; + } + const items = await ItemsHelper.fetchItems({ + ...filter, + permission + }); + let vspList = []; + let vlmList = []; + items.results.map(item => { + if (item.type === itemType.VSP) { + const { properties, ...all } = item; + vspList.push({ ...all, ...properties }); + } else { + vlmList.push(item); + } + }); + + dispatch({ + type: actionTypes.UPDATE_FILTERED_LIST, + data: { + vspList, + vlmList + } + }); + } +}; + +export default FilterActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterComponents.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterComponents.jsx new file mode 100644 index 0000000000..65ec733fd5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterComponents.jsx @@ -0,0 +1,145 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import { itemStatus } from 'sdc-app/common/helpers/ItemsHelperConstants.js'; +import Accordion from 'sdc-ui/lib/react/Accordion.js'; +import Checklist from 'sdc-ui/lib/react/Checklist.js'; +import Checkbox from 'sdc-ui/lib/react/Checkbox.js'; + +export const ItemStatus = ({ data, onDataChanged, byVendorView }) => ( + <Input + type="select" + className="catalog-filter-items-type" + data-test-id="catalog-filter-items-type" + disabled={byVendorView} + value={data.itemStatus} + onChange={e => onDataChanged({ itemStatus: e.target.value }, data)}> + <option key={itemStatus.ACTIVE} value={itemStatus.ACTIVE}> + {i18n('Active Items')} + </option> + <option key={itemStatus.ARCHIVED} value={itemStatus.ARCHIVED}> + {i18n('Archived Items')} + </option> + </Input> +); + +const FilterList = ({ title, items, groupKey, onDataChanged, data }) => { + let onChange = value => { + let obj = {}; + obj[groupKey] = { ...data[groupKey], ...value }; + onDataChanged(obj); + }; + return ( + <Accordion title={title}> + <Checklist items={items} onChange={onChange} /> + </Accordion> + ); +}; + +export const ByVendorView = ({ data, onDataChanged }) => ( + <Checkbox + label={i18n('By Vendor View')} + className="catalog-filter-by-vendor-view" + disabled={data.itemsType === itemStatus.ARCHIVED} + checked={data.byVendorView} + onChange={byVendorView => onDataChanged({ byVendorView }, data)} + data-test-id="filter-by-vendor-view" + value="" + /> +); + +export const EntityType = ({ data, onDataChanged }) => { + const items = [ + { + label: i18n('VSP'), + dataTestId: 'catalog-filter-type-vsp', + value: 'vsp', + checked: data.itemType && data.itemType.vsp + }, + { + label: i18n('VLM'), + dataTestId: 'catalog-ilter-type-vlm', + value: 'vlm', + checked: data.itemType && data.itemType.vlm + } + ]; + return ( + <FilterList + title={i18n('ENTITY TYPE')} + items={items} + onDataChanged={onDataChanged} + data={data} + groupKey="itemType" + /> + ); +}; + +export const Permissions = ({ data, onDataChanged }) => { + const items = [ + { + label: i18n('Owner'), + dataTestId: 'catalog-filter-permission-owner', + value: 'Owner', + checked: data.permission && data.permission.Owner + }, + { + label: i18n('Contributor'), + dataTestId: 'catalog-filter-permission-contributor', + value: 'Contributor', + checked: data.permission && data.permission.Contributor + } + ]; + + return ( + <FilterList + title={i18n('PERMISSIONS')} + items={items} + onDataChanged={onDataChanged} + data={data} + groupKey="permission" + /> + ); +}; + +export const OnboardingProcedure = ({ data, onDataChanged }) => { + const items = [ + { + label: i18n('Network Package'), + dataTestId: 'catalog-filter-procedure-network', + value: 'NetworkPackage', + checked: + data.onboardingMethod && data.onboardingMethod.NetworkPackage + }, + { + label: i18n('Manual'), + dataTestId: 'catalog-filter-procedure-manual', + value: 'Manual', + checked: data.onboardingMethod && data.onboardingMethod.Manual + } + ]; + + return ( + <FilterList + title={i18n('ONBOARDING PROCEDURE')} + items={items} + onDataChanged={onDataChanged} + data={data} + groupKey="onboardingMethod" + /> + ); +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterConstants.js b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterConstants.js index edfe592877..9dce52dfd6 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterConstants.js @@ -17,10 +17,6 @@ import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ - FILTER_DATA_CHANGED: null + FILTER_DATA_CHANGED: null, + UPDATE_FILTERED_LIST: null }); - -export const itemsType = { - ACTIVE: '1', - ARCHIVED: '2' -}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterMiddleware.js b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterMiddleware.js new file mode 100644 index 0000000000..8490bfe675 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterMiddleware.js @@ -0,0 +1,32 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 FilterActionHelper from './FilterActionHelper.js'; +import { actionTypes } from './FilterConstants.js'; + +const filterUpdater = store => next => action => { + if (action.type === actionTypes.FILTER_DATA_CHANGED) { + const filter = store.getState().onboard.filter; + + FilterActionHelper.updateFilteredItems(store.dispatch, { + ...filter, + ...action.deltaData + }); + } + return next(action); +}; + +export default filterUpdater; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterReducer.js index f1e857498a..28b34753a5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/FilterReducer.js @@ -14,8 +14,19 @@ * limitations under the License. */ import { actionTypes } from './FilterConstants.js'; +import { + itemStatus, + versionStatus +} from 'sdc-app/common/helpers/ItemsHelperConstants.js'; -export default (state = {}, action) => { +const defaultState = { + itemStatus: itemStatus.ACTIVE, + versionStatus: versionStatus.DRAFT, + entityType: {}, + permission: {}, + onboardingMethod: {} +}; +export default (state = defaultState, action) => { switch (action.type) { case actionTypes.FILTER_DATA_CHANGED: return { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/filter/ItemsReducer.js b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/ItemsReducer.js new file mode 100644 index 0000000000..fa1528d8d9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/filter/ItemsReducer.js @@ -0,0 +1,24 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 './FilterConstants.js'; +export default (state = { vspList: [], vlmList: [] }, action) => { + switch (action.type) { + case actionTypes.UPDATE_FILTERED_LIST: + return action.data; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx index 2cc32c2936..a416d36075 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogView.jsx @@ -83,8 +83,8 @@ const FilterCatalogHeader = () => ( ); const FeaturedCatalogHeader = featureToggle(featureToggleNames.FILTER)({ - AComp: FilterCatalogHeader, - BComp: CatalogHeader + OnComp: FilterCatalogHeader, + OffComp: CatalogHeader }); class OnboardingCatalogView extends React.Component { diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogViewWithFilter.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogViewWithFilter.jsx new file mode 100644 index 0000000000..86c437d888 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogViewWithFilter.jsx @@ -0,0 +1,147 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 DetailsCatalogView from 'sdc-app/onboarding/onboard/DetailsCatalogView.jsx'; +import VendorCatalogView from './VendorCatalogView.jsx'; +import { tabsMapping } from './OnboardingCatalogConstants.js'; +import { tabsMapping as WCTabsMapping } from 'sdc-app/onboarding/onboard/OnboardConstants.js'; + +const CatalogHeader = () => ( + <div className="catalog-header"> + <div className="catalog-header-tabs"> + <div className="catalog-header-tab active"> + {i18n('ONBOARD CATALOG')} + </div> + </div> + </div> +); + +class OnboardingCatalogView extends React.Component { + renderViewByTab(activeTab) { + const { + users, + vspOverlay, + onSelectLicenseModel, + onSelectSoftwareProduct, + onAddLicenseModelClick, + onAddSoftwareProductClick, + onVspOverlayChange, + onVendorSelect, + selectedVendor, + searchValue, + onMigrate, + filteredItems + } = this.props; + + const { vlmList, vspList } = filteredItems; + + switch (activeTab) { + case tabsMapping.ARCHIVE: + return ( + <DetailsCatalogView + VLMList={vlmList} + VSPList={vspList} + users={users} + onSelectVLM={(item, users) => + onSelectLicenseModel( + item, + users, + WCTabsMapping.CATALOG + ) + } + onSelectVSP={(item, users) => + onSelectSoftwareProduct( + item, + users, + WCTabsMapping.CATALOG + ) + } + filter={searchValue} + onMigrate={onMigrate} + /> + ); + case tabsMapping.ACTIVE: + return ( + <DetailsCatalogView + VLMList={vlmList} + VSPList={vspList} + users={users} + onAddVLM={onAddLicenseModelClick} + onAddVSP={onAddSoftwareProductClick} + onSelectVLM={(item, users) => + onSelectLicenseModel( + item, + users, + WCTabsMapping.CATALOG + ) + } + onSelectVSP={(item, users) => + onSelectSoftwareProduct( + item, + users, + WCTabsMapping.CATALOG + ) + } + filter={searchValue} + onMigrate={onMigrate} + /> + ); + case tabsMapping.BY_VENDOR: + default: + return ( + <VendorCatalogView + licenseModelList={vlmList} + users={users} + onAddVSP={onAddSoftwareProductClick} + onAddVLM={onAddLicenseModelClick} + onSelectVSP={(item, users) => + onSelectSoftwareProduct( + item, + users, + WCTabsMapping.CATALOG + ) + } + onSelectVLM={(item, users) => + onSelectLicenseModel( + item, + users, + WCTabsMapping.CATALOG + ) + } + vspOverlay={vspOverlay} + onVendorSelect={onVendorSelect} + selectedVendor={selectedVendor} + onVspOverlayChange={onVspOverlayChange} + onMigrate={onMigrate} + filter={searchValue} + /> + ); + } + } + + render() { + const { selectedVendor, catalogActiveTab: activeTab } = this.props; + return ( + <div className="catalog-wrapper"> + {!selectedVendor && <CatalogHeader />} + {this.renderViewByTab(activeTab)} + </div> + ); + } +} + +export default OnboardingCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx index bef47d5acf..12beff7a30 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/onboardingCatalog/VendorItem.jsx @@ -60,8 +60,8 @@ class VendorItem extends React.Component { </TileInfoLine> <TileInfoLine> <Button - btnType="outline-rounded" - color="dark-gray" + btnType="secondary" + className="venodor-tile-btn" onClick={e => this.handleVspCountClick(e)} data-test-id="catalog-vsp-count" disabled={!softwareProductList.length}> diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceViewWithFilter.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceViewWithFilter.jsx new file mode 100644 index 0000000000..eec59622b3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/workspace/WorkspaceViewWithFilter.jsx @@ -0,0 +1,57 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import DetailsCatalogView from '../DetailsCatalogView.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import { tabsMapping } from 'sdc-app/onboarding/onboard/OnboardConstants.js'; + +const WorkspaceView = props => { + let { + onAddLicenseModelClick, + users, + onAddSoftwareProductClick, + onSelectLicenseModel, + onSelectSoftwareProduct, + searchValue, + onMigrate, + filteredItems: { vspList, vlmList } + } = props; + + return ( + <div className="catalog-wrapper workspace-view"> + <div className="catalog-header workspace-header"> + {i18n('WORKSPACE')} + </div> + <DetailsCatalogView + VLMList={vlmList} + VSPList={vspList} + users={users} + onAddVLM={onAddLicenseModelClick} + onAddVSP={onAddSoftwareProductClick} + onSelectVLM={(item, users) => + onSelectLicenseModel(item, users, tabsMapping.WORKSPACE) + } + onSelectVSP={(item, users) => + onSelectSoftwareProduct(item, users, tabsMapping.WORKSPACE) + } + onMigrate={onMigrate} + filter={searchValue} + /> + </div> + ); +}; + +export default WorkspaceView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js index 4a2d7a2ece..25bd32e468 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -45,8 +45,11 @@ import { CommitModalType } from 'nfvo-components/panel/versionController/compone import { actionTypes as commonActionTypes } from 'sdc-app/common/reducers/PlainDataReducerConstants.js'; import versionPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js'; import { itemTypes } from 'sdc-app/onboarding/versionsPage/VersionsPageConstants.js'; -import { catalogItemStatuses } from 'sdc-app/onboarding/onboard/onboardingCatalog/OnboardingCatalogConstants.js'; import getValue from 'nfvo-utils/getValue.js'; +import { + itemStatus, + versionStatus +} from 'sdc-app/common/helpers/ItemsHelperConstants.js'; function getLicensingData(licensingData = {}) { const { licenseAgreement, featureGroups } = licensingData; @@ -112,19 +115,17 @@ function putSoftwareProductAction(id, action, version) { function fetchSoftwareProductList() { return RestAPIUtil.fetch( - `${baseUrl()}?versionFilter=${catalogItemStatuses.DRAFT}` + `${baseUrl()}?versionFilter=${versionStatus.DRAFT}` ); } function fetchArchivedSoftwareProductList() { - return RestAPIUtil.fetch( - `${baseUrl()}?Status=${catalogItemStatuses.ARCHIVED}` - ); + return RestAPIUtil.fetch(`${baseUrl()}?Status=${itemStatus.ARCHIVED}`); } function fetchFinalizedSoftwareProductList() { return RestAPIUtil.fetch( - `${baseUrl()}?versionFilter=${catalogItemStatuses.CERTIFIED}` + `${baseUrl()}?versionFilter=${versionStatus.CERTIFIED}` ); } @@ -664,9 +665,8 @@ const SoftwareProductActionHelper = { version }).then(({ inMerge, isDirty, updatedVersion }) => { if ( - (updatedVersion.status === catalogItemStatuses.CERTIFIED || - updatedVersion.archivedStatus === - catalogItemStatuses.ARCHIVED) && + (updatedVersion.status === versionStatus.CERTIFIED || + updatedVersion.archivedStatus === itemStatus.ARCHIVED) && (action === VersionControllerActionsEnum.COMMIT || action === VersionControllerActionsEnum.SYNC) ) { @@ -675,8 +675,7 @@ const SoftwareProductActionHelper = { itemId: softwareProductId }); const msg = - updatedVersion.archivedStatus === - catalogItemStatuses.ARCHIVED + updatedVersion.archivedStatus === itemStatus.ARCHIVED ? i18n('Item was Archived') : i18n('Item version already Certified'); dispatch({ diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js index 41584d94e2..9a7d257d7d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js @@ -14,9 +14,11 @@ * permissions and limitations under the License. */ import { connect } from 'react-redux'; - +import featureToggle from 'sdc-app/features/featureToggle.js'; +import { featureToggleNames } from 'sdc-app/features/FeaturesConstants.js'; import SoftwareProductCreationActionHelper from './SoftwareProductCreationActionHelper.js'; import SoftwareProductCreationView from './SoftwareProductCreationView.jsx'; +import SoftwareProductCreationViewWithFilter from './SoftwareProductCreationViewWithFilter.jsx'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import SoftwareProductActionHelper from '../SoftwareProductActionHelper.js'; import VersionsPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js'; @@ -24,6 +26,16 @@ import { itemTypes as versionItemTypes } from 'sdc-app/onboarding/versionsPage/V import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js'; import { enums, screenTypes } from 'sdc-app/onboarding/OnboardingConstants.js'; import PermissionsActionHelper from 'sdc-app/onboarding/permissions/PermissionsActionHelper.js'; +import UniqueTypesHelper from 'sdc-app/common/helpers/UniqueTypesHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import { itemType } from 'sdc-app/common/helpers/ItemsHelperConstants.js'; + +const ToggledSoftwareProductCreationView = featureToggle( + featureToggleNames.FILTER +)({ + OnComp: SoftwareProductCreationViewWithFilter, + OffComp: SoftwareProductCreationView +}); export const mapStateToProps = ({ finalizedLicenseModelList, @@ -33,7 +45,7 @@ export const mapStateToProps = ({ finalizedSoftwareProductList, softwareProduct: { softwareProductCreation, softwareProductCategories } }) => { - let { genericFieldInfo } = softwareProductCreation; + let { genericFieldInfo, vendorList = [] } = softwareProductCreation; let isFormValid = ValidationHelper.checkFormValid(genericFieldInfo); let VSPNames = {}; @@ -52,6 +64,7 @@ export const mapStateToProps = ({ disableVendor: softwareProductCreation.disableVendor, softwareProductCategories, finalizedLicenseModelList, + vendorList, isFormValid, formReady: softwareProductCreation.formReady, genericFieldInfo, @@ -99,10 +112,22 @@ export const mapActionsToProps = dispatch => { }); }, onValidateForm: formName => - ValidationHelper.validateForm(dispatch, formName) + ValidationHelper.validateForm(dispatch, formName), + isNameUnique: (value, name, formName) => + UniqueTypesHelper.isNameUnique(dispatch, { + value, + name, + formName, + errorText: `${i18n( + 'Software product by the name' + )} ${value} ${i18n('already exists')}. ${i18n( + 'Software product name must be unique' + )}`, + itemType: itemType.VSP + }) }; }; export default connect(mapStateToProps, mapActionsToProps, null, { withRef: true -})(SoftwareProductCreationView); +})(ToggledSoftwareProductCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js index 1b1fd71fef..259b50ba4f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -24,7 +24,11 @@ import { import { modalContentMapper } from 'sdc-app/common/modal/ModalContentMapper.js'; import { actionTypes } from './SoftwareProductCreationConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; - +import ItemsHelper from 'sdc-app/common/helpers/ItemsHelper.js'; +import { + itemStatus, + versionStatus +} from 'sdc-app/common/helpers/ItemsHelperConstants.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); return `${restPrefix}/v1.0/vendor-software-products/`; @@ -45,7 +49,7 @@ const SoftwareProductCreationActionHelper = { type: actionTypes.OPEN, selectedVendorId: vendorId }); - + this.loadVendorList(dispatch); dispatch({ type: modalActionTypes.GLOBAL_MODAL_SHOW, data: { @@ -78,6 +82,19 @@ const SoftwareProductCreationActionHelper = { }); return result; }); + }, + async loadVendorList(dispatch) { + const { results } = await ItemsHelper.fetchItems({ + itemStatus: itemStatus.ACTIVE, + versionStatus: versionStatus.CERTIFIED, + itemType: { + vlm: true + } + }); + dispatch({ + type: actionTypes.VENDOR_LIST_LOADED, + vendorList: results + }); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js index ad1034602a..128b3edfd9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js @@ -18,7 +18,8 @@ import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ OPEN: null, RESET_DATA: null, - SOFTWARE_PRODUCT_CREATED: null + SOFTWARE_PRODUCT_CREATED: null, + VENDOR_LIST_LOADED: null }); export const SP_CREATION_FORM_NAME = 'SPCREATIONFORM'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js index 5f70f18f75..b019248ba0 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -74,6 +74,11 @@ export default (state = {}, action) => { }, showModal: true }; + case actionTypes.VENDOR_LIST_LOADED: + return { + ...state, + vendorList: action.vendorList + }; case actionTypes.RESET_DATA: return {}; default: diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationViewWithFilter.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationViewWithFilter.jsx new file mode 100644 index 0000000000..810337dbed --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationViewWithFilter.jsx @@ -0,0 +1,329 @@ +/*! + * Copyright © 2016-2018 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 PropTypes from 'prop-types'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Validator from 'nfvo-utils/Validator.js'; +import Input from 'nfvo-components/input/validation/Input.jsx'; +import Form from 'nfvo-components/input/validation/Form.jsx'; +import GridSection from 'nfvo-components/grid/GridSection.jsx'; +import GridItem from 'nfvo-components/grid/GridItem.jsx'; + +import { SP_CREATION_FORM_NAME } from './SoftwareProductCreationConstants.js'; +import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; + +import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; +import { onboardingMethod as onboardingMethodConst } from '../SoftwareProductConstants.js'; + +const SoftwareProductPropType = PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + description: PropTypes.string, + category: PropTypes.string, + subCategory: PropTypes.string, + vendorId: PropTypes.string +}); + +class SoftwareProductCreationView extends React.Component { + static propTypes = { + data: SoftwareProductPropType, + finalizedLicenseModelList: PropTypes.array, + softwareProductCategories: PropTypes.array, + VSPNames: PropTypes.object, + usersList: PropTypes.array, + onDataChanged: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired + }; + + render() { + let { + softwareProductCategories, + data = {}, + onDataChanged, + onCancel, + genericFieldInfo, + disableVendor + } = this.props; + let { + name, + description, + vendorId, + subCategory, + onboardingMethod + } = data; + + const vendorList = this.getVendorList(); + return ( + <div className="software-product-creation-page"> + {genericFieldInfo && ( + <Form + ref={validationForm => + (this.validationForm = validationForm) + } + hasButtons={true} + onSubmit={() => this.submit()} + onReset={() => onCancel()} + labledButtons={true} + isValid={this.props.isFormValid} + submitButtonText={i18n('Create')} + formReady={this.props.formReady} + onValidateForm={() => this.validate()}> + <GridSection hasLastColSet> + <GridItem colSpan="2"> + <Input + value={name} + label={i18n('Name')} + isRequired={true} + onChange={name => + onDataChanged( + { name }, + SP_CREATION_FORM_NAME + ) + } + onBlur={this.validateIsNameUnique} + isValid={genericFieldInfo.name.isValid} + errorText={genericFieldInfo.name.errorText} + type="text" + className="field-section" + data-test-id="new-vsp-name" + /> + <Input + label={i18n('Vendor')} + type="select" + value={vendorId} + overlayPos="bottom" + isRequired={true} + disabled={disableVendor} + onChange={e => this.onSelectVendor(e)} + isValid={genericFieldInfo.vendorId.isValid} + errorText={ + genericFieldInfo.vendorId.errorText + } + className="input-options-select" + groupClassName="bootstrap-input-options" + data-test-id="new-vsp-vendor"> + {vendorList.map(vendor => ( + <option + key={vendor.title} + value={vendor.enum}> + {vendor.title} + </option> + ))} + </Input> + <Input + label={i18n('Category')} + type="select" + value={subCategory} + isRequired={true} + onChange={e => this.onSelectSubCategory(e)} + isValid={ + genericFieldInfo.subCategory.isValid + } + errorText={ + genericFieldInfo.subCategory.errorText + } + className="input-options-select" + groupClassName="bootstrap-input-options" + data-test-id="new-vsp-category"> + <option key="" value=""> + {i18n('please select…')} + </option> + {softwareProductCategories.map( + category => + category.subcategories && ( + <optgroup + key={category.name} + label={category.name}> + {category.subcategories.map( + sub => ( + <option + key={ + sub.uniqueId + } + value={ + sub.uniqueId + }>{`${ + sub.name + } (${ + category.name + })`}</option> + ) + )} + </optgroup> + ) + )} + </Input> + </GridItem> + <GridItem colSpan="2" stretch lastColInRow> + <Input + value={description} + label={i18n('Description')} + isRequired={true} + overlayPos="bottom" + onChange={description => + onDataChanged( + { description }, + SP_CREATION_FORM_NAME + ) + } + isValid={ + genericFieldInfo.description.isValid + } + errorText={ + genericFieldInfo.description.errorText + } + type="textarea" + className="field-section" + data-test-id="new-vsp-description" + /> + </GridItem> + </GridSection> + <OnboardingProcedure + genericFieldInfo={genericFieldInfo} + onboardingMethod={onboardingMethod} + onDataChanged={onDataChanged} + /> + </Form> + )} + </div> + ); + } + + getVendorList() { + let { vendorList } = this.props; + + return [{ enum: '', title: i18n('please select...') }].concat( + sortByStringProperty(vendorList, 'name').map(vendor => { + return { + enum: vendor.id, + title: vendor.name + }; + }) + ); + } + + onSelectVendor(e) { + const selectedIndex = e.target.selectedIndex; + const vendorId = e.target.options[selectedIndex].value; + this.props.onDataChanged({ vendorId }, SP_CREATION_FORM_NAME); + } + + onSelectSubCategory(e) { + const selectedIndex = e.target.selectedIndex; + const subCategory = e.target.options[selectedIndex].value; + let { softwareProductCategories, onDataChanged } = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory( + subCategory, + softwareProductCategories + ); + onDataChanged({ category, subCategory }, SP_CREATION_FORM_NAME); + } + + submit() { + let { + data: softwareProduct, + finalizedLicenseModelList, + usersList + } = this.props; + softwareProduct.vendorName = finalizedLicenseModelList.find( + vendor => vendor.id === softwareProduct.vendorId + ).name; + this.props.onSubmit(softwareProduct, usersList); + } + + validateName(value) { + const { data: { id }, VSPNames } = this.props; + const isExists = Validator.isItemNameAlreadyExistsInList({ + itemId: id, + itemName: value, + list: VSPNames + }); + + return !isExists + ? { isValid: true, errorText: '' } + : { + isValid: false, + errorText: i18n( + "Software product by the name '" + + value + + "' already exists. Software product name must be unique" + ) + }; + } + + validateIsNameUnique = e => { + const value = e.target.value; + this.props.isNameUnique(value, 'name', SP_CREATION_FORM_NAME); + }; + + validate() { + this.props.onValidateForm(SP_CREATION_FORM_NAME); + } +} + +const OnboardingProcedure = ({ + onboardingMethod, + onDataChanged, + genericFieldInfo +}) => { + return ( + <GridSection title={i18n('Onboarding procedure')}> + <GridItem colSpan={4}> + <Input + label={i18n('Network Package')} + overlayPos="top" + isValid={genericFieldInfo.onboardingMethod.isValid} + checked={ + onboardingMethod === + onboardingMethodConst.NETWORK_PACKAGE + } + errorText={genericFieldInfo.onboardingMethod.errorText} + onChange={() => + onDataChanged( + { + onboardingMethod: + onboardingMethodConst.NETWORK_PACKAGE + }, + SP_CREATION_FORM_NAME + ) + } + type="radio" + data-test-id="new-vsp-creation-procedure-heat" + /> + </GridItem> + <GridItem colSpan={4}> + <Input + label={i18n('Manual')} + overlayPos="bottom" + checked={onboardingMethod === onboardingMethodConst.MANUAL} + isValid={genericFieldInfo.onboardingMethod.isValid} + errorText={genericFieldInfo.onboardingMethod.errorText} + onChange={() => + onDataChanged( + { onboardingMethod: onboardingMethodConst.MANUAL }, + SP_CREATION_FORM_NAME + ) + } + type="radio" + data-test-id="new-vsp-creation-procedure-manual" + /> + </GridItem> + </GridSection> + ); +}; + +export default SoftwareProductCreationView; |