diff options
Diffstat (limited to 'openecomp-ui')
150 files changed, 3119 insertions, 1547 deletions
diff --git a/openecomp-ui/.gitignore b/openecomp-ui/.gitignore index 2a11210d95..13e532c095 100644 --- a/openecomp-ui/.gitignore +++ b/openecomp-ui/.gitignore @@ -6,6 +6,9 @@ debug.log dist node_modules devConfig.json +proxy-server.js +runLocalFE.cmd +runLocalFE.js .npmrc test/coverage npm-debug.log diff --git a/openecomp-ui/devConfig.defaults.json b/openecomp-ui/devConfig.defaults.json index 46f0d189e8..99380b1e50 100644 --- a/openecomp-ui/devConfig.defaults.json +++ b/openecomp-ui/devConfig.defaults.json @@ -1,6 +1,6 @@ { "port": 9000, - "proxyATTTarget": null, + "proxyCatalogTarget": null, "proxyTarget": null, "bundles": { "bundle": ["sdc-app/sdc.app.jsx"], diff --git a/openecomp-ui/external-resources/healthcheck/v1.0/healthcheck b/openecomp-ui/external-resources/healthcheck/healthcheck index 6a3dd41755..e17838f246 100644 --- a/openecomp-ui/external-resources/healthcheck/v1.0/healthcheck +++ b/openecomp-ui/external-resources/healthcheck/healthcheck @@ -1,10 +1,10 @@ { - "sdcVersion": "1.0-SNAPSHOT", + "sdcVersion": "{VERSION}", "componentsInfo": [ { "healthCheckComponent": "FE", "healthCheckStatus": "UP", - "version": "1.0-SNAPSHOT", + "version": "{VERSION}", "description": "OK" } ] diff --git a/openecomp-ui/gulpfile.js b/openecomp-ui/gulpfile.js index eb755a2be0..6d45a0b374 100644 --- a/openecomp-ui/gulpfile.js +++ b/openecomp-ui/gulpfile.js @@ -33,14 +33,16 @@ const path = { svgSrc: './resources/images/svg/*.svg', appinf: './webapp-onboarding/**/*.*', jetty: './webapp-onboarding/WEB-INF/jetty-web.xml', + healthCheckInput: './external-resources/healthcheck/healthcheck', srcDir: './src/', // output output: dist, css: dist + '/css', svg: dist + '/resources/images/svg', appinf_output: dist + '/webapp-onboarding', + healthCheckOutput: dist + '/v1.0', // war - war: [dist + '/index.html', dist + '/punch-outs*.js', dist + '/**/*.{css,png,svg,eot,ttf,woff,woff2,otf}', dist + '/**/*(config.json)', dist + '/webapp-onboarding/**'], + war: [dist + '/index.html', dist + '/punch-outs*.js', dist + '/**/*.{css,png,svg,eot,ttf,woff,woff2,otf}', dist + '/**/*(config.json)', dist + '/webapp-onboarding/**', dist + '/**/*(healthcheck)'], heatWar: [dist + '/heat.html', dist + '/heat-validation_en.js', dist + '/**/*.{css,png,svg,eot,ttf,woff,woff2,otf}', dist + '/**/*(config.json)', 'webapp-heat-validation/**'], wardest: dist, // storybook @@ -69,6 +71,17 @@ taskMaker.defineTask('watch', {taskName: 'watch-stuff', src: [path.json, path.in //TODO: delete this task after gulp-css-usage support for SCSS files taskMaker.defineTask('sass', {taskName: 'sass', src: path.scss, dest: path.css, config: {outputStyle: 'compressed'}}); +// copy the healthcheck file and replace the version with command line argument +gulp.task('healthcheck', function(){ + let args = process.argv; + let versionArg = args.find(arg => arg.startsWith('--version')); + let version = versionArg && versionArg.slice(versionArg.indexOf('=') + 1); + if (versionArg) { + gulp.src(path.healthCheckInput) + .pipe(replace('{VERSION}', version)) + .pipe(gulp.dest(path.healthCheckOutput)); + } +}); // update the app-context for the web-xml file to the value from the config gulp.task('app-context', function(){ @@ -86,7 +99,7 @@ gulp.task('copy-stuff', callback => runSequence(['copy-json', 'copy-index.html', // minimum build for dev gulp.task('dev', callback => runSequence('clean', 'copy-stuff', callback)); // build procedure for war file -gulp.task('build', callback => runSequence('clean', 'copy-stuff', 'prod', ['compress-war', 'compress-heat-war'], callback)); +gulp.task('build', callback => runSequence('clean', 'copy-stuff', 'healthcheck', 'prod', ['compress-war', 'compress-heat-war'], callback)); // default build is set to 'dev' gulp.task('default', ['dev']); // creating the webpack tasks for the production build diff --git a/openecomp-ui/package.json b/openecomp-ui/package.json index 0e1e66b80e..f18dd75329 100644 --- a/openecomp-ui/package.json +++ b/openecomp-ui/package.json @@ -8,8 +8,8 @@ "start": "gulp dev && webpack-dev-server --progress", "build": "gulp build", "css-usage": "gulp css-usage", - "static-keys-bundle" : "gulp static-keys-bundle", - "check-keys-against-bundles" : "gulp static-keys-bundle-with-report", + "static-keys-bundle": "gulp static-keys-bundle", + "check-keys-against-bundles": "gulp static-keys-bundle-with-report", "test": "jest", "test-failedTestReport": "jest --json | node test-utils/failedTestReport.js", "test-dev": "jest --watch", @@ -21,6 +21,7 @@ "dependencies": { "classnames": "^2.2.5", "core-js": "^2.4.0", + "d3": "^3.5.16", "dox-sequence-diagram-ui": "file:../dox-sequence-diagram-ui", "intl": "^1.0.1", "intl-format-cache": "^2.0.5", @@ -39,7 +40,7 @@ "react-sortable": "^1.2.0", "redux": "^3.3.1", "restful-js": "^0.7.0", - "sdc-ui": "^1.5.12", + "sdc-ui": "1.6.2", "uuid-js": "^0.7.5", "validator": "^4.3.0" }, @@ -109,6 +110,7 @@ "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test-utils/fileMock.js", "\\.(css|scss)$": "<rootDir>/test-utils/styleMock.js", "^nfvo-utils/RestAPIUtil.js$": "<rootDir>/test-utils/MockRest.js", + "^sdc-ui/lib/react/SVGIcon.js$": "<rootDir>/test-utils/MockSVGIcon.js", "^nfvo-utils(.*)$": "<rootDir>/src/nfvo-utils$1", "^nfvo-components(.*)$": "<rootDir>/src/nfvo-components$1", "^sdc-app(.*)$": "<rootDir>/src/sdc-app$1", diff --git a/openecomp-ui/proxy-server.js b/openecomp-ui/proxy-server.js deleted file mode 100644 index 4733d38a89..0000000000 --- a/openecomp-ui/proxy-server.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict'; - -const proxy = require('http-proxy-middleware'); - -let localDevConfig = {}; -try { - localDevConfig = require('./devConfig'); -} catch (e) {} -const devConfig = Object.assign({}, require('./devConfig.defaults'), localDevConfig); -let devPort = process.env.PORT || devConfig.port; - -let jsonConfig = { - "appContextPath" : "/onboarding" -}; - -try { - jsonConfig = require('./src/sdc-app/config/config.json'); -} catch (e) { - console.log('could not load config. using deault value instead'); -} - -module.exports = function (server) { - let proxyConfigDefaults = { - changeOrigin: true, - secure: false, - onProxyRes: (proxyRes, req, res) => { - let setCookie = proxyRes.headers['set-cookie']; - if (setCookie) { - setCookie[0] = setCookie[0].replace(/\bSecure\b(; )?/, ''); - } - if (proxyRes.statusCode === 302 && proxyRes.headers.location.indexOf('login') > -1) { - proxyRes.headers.location = `http://localhost:${devPort}/sdc1#/onboardVendor`; - res.setHeader('Set-Cookie', [ - 'HTTP_CSP_EMAIL=csantana@sdc.com', - 'HTTP_CSP_FIRSTNAME=Carlos', - 'HTTP_CSP_LASTNAME=Santana', - 'HTTP_CSP_WSTYPE=Intranet', - 'HTTP_IV_REMOTE_ADDRESS=0.0.0.0', - 'HTTP_IV_USER=cs0008', - 'USER_ID=cs0008' - ]); - } - } - }; - - let middlewares = [ - (req, res, next) => { - if (req.url.indexOf('/proxy-designer1') > -1) { - req.url = req.url.replace('/proxy-designer1', ''); - } - - if (req.url.indexOf(jsonConfig.appContextPath + '/resources') > -1) { - req.url = req.url.replace(jsonConfig.appContextPath, ''); - } - - let match = req.url.match(/^(.*)_en.js$/); - let newUrl = match && match[1] + '.js'; - if (newUrl) { - console.log(`REWRITING URL: ${req.url} -> ${newUrl}`); - req.url = newUrl; - } - next(); - } - ]; - - // standalon back-end (proxyTarget) has higher priority, so it should be first - if (devConfig.proxyTarget) { - middlewares.push( - proxy(['/api', '/onboarding-api', '/sdc1/feProxy/onboarding-api'], Object.assign({}, proxyConfigDefaults, { - target: devConfig.proxyTarget, - pathRewrite: { - '/sdc1/feProxy/onboarding-api': '/onboarding-api' - } - })) - ); - } - - // ATT environment (proxyATTTarget) has lower priority, so it should be second - if (devConfig.proxyATTTarget) { - middlewares.push( - proxy(['/sdc1', '/onboarding-api', '/scripts', '/styles'], Object.assign({}, proxyConfigDefaults, { - target: devConfig.proxyATTTarget, - pathRewrite: { - // Workaround for some weird proxy issue - '/sdc1/feProxy/onboarding-api': '/sdc1/feProxy/onboarding-api', - '/onboarding-api': '/sdc1/feProxy/onboarding-api' - } - })) - ); - } - server.use(middlewares); -}; diff --git a/openecomp-ui/resources/scss/_components.scss b/openecomp-ui/resources/scss/_components.scss index c70b914187..7c726aa8dd 100644 --- a/openecomp-ui/resources/scss/_components.scss +++ b/openecomp-ui/resources/scss/_components.scss @@ -16,7 +16,6 @@ @import "components/submitErrorResponse"; @import "components/expandableInput"; @import "components/grid"; -@import "components/icon"; @import "components/activityLog"; @import "components/selectActionTable"; @import "components/datepicker"; diff --git a/openecomp-ui/resources/scss/_modules.scss b/openecomp-ui/resources/scss/_modules.scss index 317f90800f..405e91a61b 100644 --- a/openecomp-ui/resources/scss/_modules.scss +++ b/openecomp-ui/resources/scss/_modules.scss @@ -1,4 +1,5 @@ @import "modules/licenseModelOverview"; +@import "modules/licenseModelLimits"; @import "modules/licenseAgreement"; @import "modules/featureGroup"; @import "modules/entitlementPools"; diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss b/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss index 9301f1ed39..ad6b4f8aca 100644 --- a/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss +++ b/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss @@ -1,6 +1,7 @@ .modal-content { .modal-header { border-top: 3px solid $blue; + padding-left: 50px; .modal-title { @extend .heading-2; } diff --git a/openecomp-ui/resources/scss/common/_utils.scss b/openecomp-ui/resources/scss/common/_utils.scss index dce623e15d..54f8b977a3 100644 --- a/openecomp-ui/resources/scss/common/_utils.scss +++ b/openecomp-ui/resources/scss/common/_utils.scss @@ -219,14 +219,10 @@ $browserPrefixes: webkit moz o ms; width: $size; height: $size; background: $bgcolor; - border: 3px solid $bgcolor; - &:after { - content: $content; - position: relative; - left: 9px; - top: 9px; - @extend .circle-icon-text; - } + border: 3px solid $bgcolor; + display: flex; + align-items: center; + justify-content: center; } /**/ @@ -306,3 +302,12 @@ $browserPrefixes: webkit moz o ms; @mixin border-shadow($xShadow: 0.545px, $yShadow: 0.839px, $blur: 4px, $spread: 0, $color: $light-gray, $opacity: 0.2) { @include box-shadow($xShadow $yShadow $blur $spread rgba($color, $opacity)); } + +/* percent-plus-value */ +// @mixin +// Calculate length property (e.g. width, margin) by adding a value (e.g. in pixels) +// to a percentage of container height/width +@mixin percent-plus-value($property, $value, $percent: 100%) { + $string: 'calc(' + $percent + ' + ' + $value + ')'; + #{$property}: unquote($string); +} diff --git a/openecomp-ui/resources/scss/common/_variables.scss b/openecomp-ui/resources/scss/common/_variables.scss index 2162b0b1b0..3a3178c6b0 100644 --- a/openecomp-ui/resources/scss/common/_variables.scss +++ b/openecomp-ui/resources/scss/common/_variables.scss @@ -47,14 +47,23 @@ $desktop-min-width: 1824px; /* Textures */ $images-folder-name: "../images"; +/* +$plus-circle-icon: $images-folder-name + "/plus-circle-icon.svg"; +$interface-icon: $images-folder-name + "/interface.svg"; +$sdc-logo: $images-folder-name + "/logo.svg"; +$warning-icon: $images-folder-name + "/warning.svg"; +*/ /* Icons */ $icons-folder: "../images/icons"; -$artifacts-icon: $images-folder-name + "/artifacts_icon.png"; -$check-icon: $images-folder-name + "/v_icon.png"; -$base-module-icon: $images-folder-name + "/base_icon.png"; -$module-icon: $images-folder-name + "/module_icon.png"; +/* +$plus-circle-icon: $images-folder-name + "/plus-circle-icon.svg"; +$interface-icon: $images-folder-name + "/interface.svg"; +$sdc-logo: $images-folder-name + "/logo.svg"; +$warning-icon: $images-folder-name + "/warning.svg"; +*/ $pencil-icon: $images-folder-name + "/pencil_icon-01.svg"; +/* $vc-check-in-icon: $icons-folder + "/checked_in.png"; $vc-checkout-icon: $icons-folder + "/checked_out.png"; $vc-revert-icon: $icons-folder + "/reverticon.png"; @@ -66,11 +75,12 @@ $vc-submit-disabled-icon: $icons-folder + "/submit_icon_disable.png"; $trash-icon: $images-folder-name + "/trash_icon.png"; $download-icon: $images-folder-name + "/download_icon.png"; $upload-icon: $images-folder-name + "/upload_icon.png"; +*/ - -/* catalog icons */ +/***** +// catalog icons $back-icon: $icons-folder + "/back_icon.png"; -/* validation icons */ +// validation icons $artifacts-selected-icon: $icons-folder + "/artifacts_blue_icon.png"; $artifacts-regular-icon: $icons-folder + "/artifacts_grey_icon.png"; $chevron_down: $icons-folder + "/down_chevron.png"; @@ -93,7 +103,7 @@ $heat-selected-icon: $icons-folder + "/nested_HEAT_icon_blue.png"; $env-icon: $icons-folder + "/env_icon.png"; $env-selected-icon: $icons-folder + "/env_icon_blue.png"; -/* vlm summary icons */ +// vlm summary icons $vlm-summary-plus-blue: $icons-folder + "/plus_vlm_summary_icon_blue.png"; $vlm-summary-plus: $icons-folder + "/plus_vlm_summary_icon.png"; @@ -102,4 +112,5 @@ $vlm-summary-orphans: $icons-folder + "/orphans_grey_icon.png"; $vlm-summary-orphans-blue: $icons-folder + "/orphans_blue_icon-n.png"; $vlm-summary-used: $icons-folder + "/vlm_list_view_grey_icon.png"; $vlm-summary-used-blue: $icons-folder + "/vlm_list_view_blue_icon.png"; +*****/ diff --git a/openecomp-ui/resources/scss/components/_activityLog.scss b/openecomp-ui/resources/scss/components/_activityLog.scss index 9ada804934..5e9418ed7d 100644 --- a/openecomp-ui/resources/scss/components/_activityLog.scss +++ b/openecomp-ui/resources/scss/components/_activityLog.scss @@ -39,11 +39,7 @@ $message-info-icon-size: 16px; .svg-icon-wrapper { float: right; } - .checkCircle { - fill: $green; - width: 16px; - height: 16px; - } + .status-icon.false:after { @include status-icon-class; float: right; diff --git a/openecomp-ui/resources/scss/components/_datepicker.scss b/openecomp-ui/resources/scss/components/_datepicker.scss index 593bb09d7d..d51a1b690e 100644 --- a/openecomp-ui/resources/scss/components/_datepicker.scss +++ b/openecomp-ui/resources/scss/components/_datepicker.scss @@ -1,5 +1,6 @@ .customized-date-picker { margin-bottom: 24px; + display: flex; .date-picker-label { &.required { &:before { @@ -13,27 +14,38 @@ color: $dark-gray; margin-bottom: 8px; } + .react-datepicker__input-container { + flex: 1; + cursor: pointer; + } .datepicker-custom-input { display: flex; justify-content: space-between; - width: 243px; + align-items: center; height: 30px; border-radius: 2px; color: $dark-gray; border: 1px solid $light-gray; - padding: 6px 12px; + padding: 6px 8px 6px 12px; + .clear-input { + margin-left: auto; + margin-right: 8px; + .svg-icon { + fill: transparent; + height: 8px; + width: 8px; + } + } &:hover { border-color: $gray; .clear-input { - &:before { - cursor: pointer; - content: 'x'; + .svg-icon { + fill: initial; } } } .datepicker-text { cursor: pointer; - width: 170px; @extend .body-1; &.placeholder { color: $light-gray; @@ -43,9 +55,13 @@ .calendar-input { fill: $light-gray; } + .svg-icon-wrapper.calendar { + .svg-icon { + fill: $dark-gray; + width: 17px; + height: 17px; + } + } } - - - } diff --git a/openecomp-ui/resources/scss/components/_dualListBox.scss b/openecomp-ui/resources/scss/components/_dualListBox.scss index 4a1a940c34..4353ab39e8 100644 --- a/openecomp-ui/resources/scss/components/_dualListBox.scss +++ b/openecomp-ui/resources/scss/components/_dualListBox.scss @@ -7,8 +7,7 @@ } .dual-search-multi-select-section { - $multi-select-box-width: 398px; - width: $multi-select-box-width; + flex: 1; .dual-text-box-search-title, .dual-list-box-multi-select-text { @extend .body-1; color: $dark-gray; diff --git a/openecomp-ui/resources/scss/components/_expandableInput.scss b/openecomp-ui/resources/scss/components/_expandableInput.scss index 52b72d7c91..2484a73e8a 100644 --- a/openecomp-ui/resources/scss/components/_expandableInput.scss +++ b/openecomp-ui/resources/scss/components/_expandableInput.scss @@ -1,24 +1,21 @@ .expandable-input-top { display: flex; height: 22px; - .svg-icon-wrapper { - height: 17px; - width: 17px; - } .expandable-input-wrapper { display: flex; - .svg-icon.search { - height: 17px; - width: 17px; - } &.closed { - .svg-icon.search { - transition: fill 0.5s ease-in; - fill: $blue; - cursor: pointer; - &:hover { + .svg-icon + { + &.__search { + height: 17px; + width: 17px; transition: fill 0.5s ease-in; - fill: $dark-blue; + fill: $blue; + cursor: pointer; + &:hover { + transition: fill 0.5s ease-in; + fill: $dark-blue; + } } } } @@ -26,17 +23,21 @@ .svg-icon-wrapper { margin-left: 3px; } - .svg-icon.search { - fill: $dark-blue; + .svg-icon { + &.__search { + height: 17px; + width: 17px; + fill: $dark-blue; + } } - .svg-icon.close { - margin-left: 7px; - height: 10px; - width: 10px; - opacity: 0.6; - fill: $dark-gray; - &:hover { - opacity: 1; + .svg-icon { + &.__close { + margin-left: 7px; + opacity: 0.6; + fill: $dark-gray; + &:hover { + opacity: 1; + } } } } diff --git a/openecomp-ui/resources/scss/components/_notifications.scss b/openecomp-ui/resources/scss/components/_notifications.scss index 7165e57ef9..5b10dee0ee 100644 --- a/openecomp-ui/resources/scss/components/_notifications.scss +++ b/openecomp-ui/resources/scss/components/_notifications.scss @@ -18,6 +18,12 @@ .modal-content .modal-header { border-top: 3px solid $red; } + } + + &.negative { + .modal-content .modal-header { + border-top: 3px solid $red; + } } &.warning { diff --git a/openecomp-ui/resources/scss/components/_selectActionTable.scss b/openecomp-ui/resources/scss/components/_selectActionTable.scss index 4e42b6ce5b..fa8eb3110c 100644 --- a/openecomp-ui/resources/scss/components/_selectActionTable.scss +++ b/openecomp-ui/resources/scss/components/_selectActionTable.scss @@ -1,15 +1,13 @@ .select-action-table-view { .svg-icon-wrapper { flex-direction: row; - - &::before { - content:""; - } .svg-icon { - margin-left: 5px; - margin-right: 5px; - width:16px; - height:16px; + &:not(.__plus) { + margin-left: 5px; + margin-right: 5px; + width:16px; + height:16px; + } } } .dummy-icon { @@ -57,21 +55,11 @@ display: flex; flex-direction: row; margin-bottom: 14px; - .svg-icon-wrapper.trashO { + .svg-icon-wrapper.hideDelete { .svg-icon { - fill: $dark-gray; + fill: $white; } } - .svg-icon-wrapper.errorCircle { - .svg-icon { - fill: $red; - } - } - .svg-icon-wrapper.checkCircle { - .svg-icon { - fill: $green; - } - } .select-action-table-row { display: flex; flex: 1; @@ -80,6 +68,7 @@ &.has-error { border-color: $red; } + .select-action-table-cell { flex: 1; border-right: 1px solid; @@ -127,10 +116,9 @@ border-bottom: 1px solid $blue; overflow: auto; .Select-menu { - display: inline-block; .Select-option { - width: 100%; - display: inline-block; + overflow: hidden; + text-overflow: ellipsis; border-bottom: 1px solid $light-gray; &:hover { background-color: $blue; diff --git a/openecomp-ui/resources/scss/components/_submitErrorResponse.scss b/openecomp-ui/resources/scss/components/_submitErrorResponse.scss index b917dfefeb..f045038193 100644 --- a/openecomp-ui/resources/scss/components/_submitErrorResponse.scss +++ b/openecomp-ui/resources/scss/components/_submitErrorResponse.scss @@ -26,14 +26,19 @@ background-color: $tlv-gray; padding: 5px; cursor: pointer; - .chevronDown { - width:10px; - height:10px; - margin-right: 10px; - &.right { - transform: rotate(270deg); + .svg-icon { + &.__chevronDown { + margin-right: 10px; } } + .collapse-right { + .svg-icon { + &.__chevronDown { + transform: rotate(270deg); + } + } + + } } .error-code-list-item { diff --git a/openecomp-ui/resources/scss/components/_validationForm.scss b/openecomp-ui/resources/scss/components/_validationForm.scss index 46269ad699..e1adb5650f 100644 --- a/openecomp-ui/resources/scss/components/_validationForm.scss +++ b/openecomp-ui/resources/scss/components/_validationForm.scss @@ -7,12 +7,10 @@ form { .validation-radio-wrapper { position: relative; } - .nav-tabs { + .sdc-tabs { position: relative; - .invalid-tab:not(.active) { - a { + .invalid-tab:not(.sdc-tab-active) { color: $red; - } } } .validation-error-message { @@ -77,7 +75,7 @@ form { flex: 0.2; content: ' '; } - @media (min-width: 1349px) { + @media (min-width: 1389px) { &.add-line-break { .control-label { &:after { diff --git a/openecomp-ui/resources/scss/components/_versionController.scss b/openecomp-ui/resources/scss/components/_versionController.scss index b454d3496b..57610986c3 100644 --- a/openecomp-ui/resources/scss/components/_versionController.scss +++ b/openecomp-ui/resources/scss/components/_versionController.scss @@ -59,56 +59,33 @@ margin-right: 20px; padding-bottom: 5px; - .versionControllerLockClosed { + + .svg-icon { fill: $dark-gray; - width: 21px; - height: 23px; - margin-top: -3px; - &.disabled { - fill: $light-gray; - } &:hover { fill: $black; } - } - .versionControllerLockOpen { - fill: $dark-gray; - width: 24px; - height: 28px; - margin-top: -6px; - &:hover { - fill: $black; + &.__versionControllerLockClosed { + margin-top: -3px; } - } - .versionControllerSubmit { - fill: $blue; - &.disabled { - fill: $light-gray; + &.__versionControllerLockOpen { + margin-top: -6px; } - &:hover { - fill: $dark-blue; + &.__versionControllerSubmit { + fill: $blue; + &:hover { + fill: $dark-blue; + } + &.disabled { + fill: $dark-gray; + } } } - - .versionControllerRevert { - fill: $dark-gray; - &.disabled { - fill: $light-gray; - } - &:hover { - fill: $black; - } - } - .versionControllerSave { - fill: $dark-gray; - &.disabled { - fill: $light-gray; - } - &:hover { - fill: $black; - } } - } + + + + } .vc-nav-item-close { display: flex; diff --git a/openecomp-ui/resources/scss/modules/_entitlementPools.scss b/openecomp-ui/resources/scss/modules/_entitlementPools.scss index 0e0a72a387..ad0bb77e8d 100644 --- a/openecomp-ui/resources/scss/modules/_entitlementPools.scss +++ b/openecomp-ui/resources/scss/modules/_entitlementPools.scss @@ -1,6 +1,5 @@ -.entitlement-pools-list-editor { - +.entitlement-pools-list-editor { .list-editor-view-list { .list-editor-item-view { min-height: 110px; @@ -8,13 +7,9 @@ } .list-editor-item-view-field { - .entitlement-pools-count, .entitlement-parameters, .contract-number, .type{ + .entitlement-pools-count, .contract-number, .type{ color: $purple; } - .entitlement-parameters { - @include ellipsis; - margin-bottom: 2px; - } .entitlement-pools-count { @extend .heading-1; margin-top: -10px; @@ -25,22 +20,29 @@ } .entitlement-pools-modal { - .validation-form-content { - padding: 50px; - } .modal-body { padding: 0; } + .sdc-tab-content { + margin: 0; + } .entitlement-pools-form { - .tab-content { - padding: 50px; - } .threshold-section { display: flex; justify-content: space-between; .validation-input-wrapper { flex: 0 0 46%; } + } + .date-section { + display: flex; + justify-content: space-between; + .validation-input-wrapper { + flex: 0 0 46%; + } + } + .validation-form-content { + padding: 20px 50px; } } .validation-buttons { diff --git a/openecomp-ui/resources/scss/modules/_featureGroup.scss b/openecomp-ui/resources/scss/modules/_featureGroup.scss index 8c45d8ec58..5f98ed2c05 100644 --- a/openecomp-ui/resources/scss/modules/_featureGroup.scss +++ b/openecomp-ui/resources/scss/modules/_featureGroup.scss @@ -1,23 +1,37 @@ .feature-groups-list-editor { - .list-editor-view-list { - .list-editor-item-view { - min-height: 110px; - height: 110px; - } - .list-editor-item-view-field { - .feature-groups-count-field { - display: inline-block; - &:first-child { - margin-right: 95px; - } + .list-editor-view{ + .list-editor-view-list { + .list-editor-item-view { + min-height: 110px; + height: 110px; } - .feature-groups-count-ep { - @extend .heading-1; - color: $light-blue; - } - .feature-groups-count-lk { - @extend .heading-1; - color: $light-green; + .list-editor-item-view-content { + .list-editor-item-view-field { + &.smaller-field { + flex: 0.35; + } + .feature-groups-count-field { + display: inline-block; + &:first-child { + margin-right: 95px; + } + } + .feature-groups-count-ep { + @extend .heading-1; + color: $light-blue; + } + .feature-groups-mrn-ep { + @extend .body-1; + color: $light-blue; + } + .feature-groups-count-lk { + @extend .heading-1; + color: $light-green; + } + .title-no-wrap { + white-space: nowrap; + } + } } } } @@ -32,6 +46,14 @@ margin-top: 55px; color: $dark-gray; } + .validation-form-content { + padding: 0; + } + + .nav.nav-tabs { + padding-left: 50px; + } + .tab-content { padding: 50px; .field-section { diff --git a/openecomp-ui/resources/scss/modules/_licenseKeyGroup.scss b/openecomp-ui/resources/scss/modules/_licenseKeyGroup.scss index 5de757bf64..d072849c08 100644 --- a/openecomp-ui/resources/scss/modules/_licenseKeyGroup.scss +++ b/openecomp-ui/resources/scss/modules/_licenseKeyGroup.scss @@ -16,23 +16,3 @@ } } } - -.license-key-groups-modal { - .modal-body { - padding: 0; - } - .license-key-groups-form { - .validation-form-content { - padding: 50px; - .field-section { - @extend .body-2-semibold; - margin-bottom: 23px; - width: 400px; - color: $black; - } - } - .validation-buttons { - padding: 20px 50px; - } - } -} diff --git a/openecomp-ui/resources/scss/modules/_licenseModel.scss b/openecomp-ui/resources/scss/modules/_licenseModel.scss index 3d65be6274..fab091fe67 100644 --- a/openecomp-ui/resources/scss/modules/_licenseModel.scss +++ b/openecomp-ui/resources/scss/modules/_licenseModel.scss @@ -1,20 +1,53 @@ -.license-model-type-modal { +.license-model-modal { .modal-body { padding: 0; } - .validation-form-content { - padding: 50px; - .field-section { - @extend .body-2-semibold; - margin-bottom: 23px; - width: 400px; - color: $black; + .modal-header { + border-bottom: none; + padding-bottom: 30px; + } + .modal-body { + padding: 0; + } + .sdc-tabs-list { + padding-left: 50px; + border-bottom: 1px solid $tlv-light-gray; + display: flex; + align-items: baseline; + } + .sdc-tab-content { + margin-top: 0; + } + .add-limit-button { + margin-left: auto; + margin-right: 50px; + padding-right: 0; + text-align: end; + } + .license-model-form { + .validation-form-content { + padding: 20px 50px; + } + .validation-buttons { + padding: 20px 50px; } - textarea { - height: 107px; + &.license-agreement-form, &.feature-group-form { + .validation-form-content { + padding: 0; + } + .sdc-tab-content { + padding: 20px 50px; + } + } + } + .license-model-modal-buttons { + padding: 21px 50px; + display: flex; + justify-content: flex-end; + background-color: $tlv-gray; + margin-top: 2px; + .sdc-button { + margin-left: 20px; } - } - .validation-buttons { - padding: 20px 50px; - } + } } diff --git a/openecomp-ui/resources/scss/modules/_licenseModelLimits.scss b/openecomp-ui/resources/scss/modules/_licenseModelLimits.scss new file mode 100644 index 0000000000..32fd128f2e --- /dev/null +++ b/openecomp-ui/resources/scss/modules/_licenseModelLimits.scss @@ -0,0 +1,121 @@ +.license-model-limits-view { + max-height: 490px; + overflow: auto; + + .limit-editor-title { + padding: 10px 50px; + background-color: $blue; + color: $white; + + } + .list-editor-view-add-title { + margin-right: 20px; + } + + .no-limits-text { + padding-left: 50px; + } + + .list-editor-view { + .list-editor-view-header { + border-bottom: none; + padding-top: 30px; + padding-bottom: 0; + } + + .list-editor-view-list-scroller { + margin-top: 0; + margin-bottom: 30px; + } + .list-editor-view-list { + width: 100%; + .list-editor-item-view { + min-height: 50px; + height: 50px; + background-color: $tlv-light-gray; + border-color: transparent; + margin: 1px 0; + .list-editor-item-view-content { + padding-left: 0; + } + .svg-icon-wrapper.trashO { + margin-bottom: 0; + margin-right: 50px; + .svg-icon { + fill: $gray; + height: 16px; + width: 16px; + &:hover { + fill: $dark-gray; + } + } + } + &.selectable { + &:hover { + background-color: darken($tlv-light-gray, 4%); + cursor: pointer; + } + } + &:hover { + border-color: transparent; + cursor: default; + } + .list-editor-item-view-content { + .list-editor-item-view-field { + display: flex; + align-items: center; + white-space: nowrap; + + &.limit-name { + .text.name { + @extend .body-1-semibold; + color: $blue; + text-transform: uppercase; + padding: 0px 10px; + } + + border-right: 1px solid $light-gray; + margin-right: 22px; + flex: 0.4; + display: flex; + justify-content: center; + } + + &.limit-description { + max-width: 300px; + margin-right: 22px; + } + + &.limit-metric-details { + max-width: 300px; + } + + .text.description, .text.name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + } + } + } + } + .limit-editor{ + .limit-editor-form { + .limit-editor-form-grid-section { + padding-bottom: 0; + } + .validation-form-content { + padding: 21px 45px; + } + .limit-editor-buttons { + display: flex; + justify-content: flex-end; + .sdc-button { + margin-left: 20px; + } + } + } + } +}
\ No newline at end of file diff --git a/openecomp-ui/resources/scss/modules/_licenseModelOverview.scss b/openecomp-ui/resources/scss/modules/_licenseModelOverview.scss index 5c74e82d83..15bb9a7033 100644 --- a/openecomp-ui/resources/scss/modules/_licenseModelOverview.scss +++ b/openecomp-ui/resources/scss/modules/_licenseModelOverview.scss @@ -1,3 +1,21 @@ +$list-indentation: 20px; +$arrow-margin: 30px; +$icon-margin: 30px; +$arrow-element-width: 30px; +$list-item-padding: 15px; + +@mixin overview-tile-shadow() { + @include box-shadow(0.5px 0.8px 4px 0 rgba(24, 24, 25, 0.05)); +} + +// @mixin vlm-list-item-inset($color) { +// @include box-shadow(inset 6px 0 0 0 $color); +// } + +$la-color: $dark-blue; +$fg-color: $blue; +$lkg-ep-color: $light-blue; + .license-model-overview { .overview-top-section { .overview-title{ @@ -20,60 +38,69 @@ .vendor-data-view { @extend .flex-column; background-color: $tlv-gray; - padding: 20px 30px; + padding: 13px 30px; border: 1px solid $tlv-light-gray; - @include border-shadow(); + @include overview-tile-shadow(); .vendor-title { margin-top:5px; } .vendor-name { @extend .heading-4-semibold; text-transform: none; - padding-bottom: 15px; + padding-bottom: 10px; border-bottom: 1px solid $tlv-light-gray; } - .vendor-description { + .vendor-description, .vendor-description-readonly { @extend .flex; @extend .body-1; justify-content: space-between; - margin-top: 10px; overflow: hidden; + position: relative; + flex: initial; + } + + &:not(.read-only) .vendor-description { border: 1px solid transparent; + width: 100%; + padding: 2px 0 2px 6px; + margin-top: 10px; position: relative; - left: -6px; - width: 101%; - + .svg-icon-wrapper { + position: absolute;; + right:0; + top:0; + opacity: 0; + } + $hover-padding-right: 16px; + @include percent-plus-value($property: width, $percent: 100%, $value: -$hover-padding-right); // compensate for padding added on hover &:hover { - border: 1px solid $tlv-light-gray; - background-position: 99% 12%; - background-size: 15px; - padding-right: 20px; - background-image: url($pencil-icon); - background-repeat: no-repeat; + padding-right: $hover-padding-right; + border: 1px solid $light-gray; cursor: pointer; + + background-color: $white; + .svg-icon-wrapper { + opacity: 1; + z-index: 10; + } + .description-data:after { + background: white; + } + .description-data { + width: 100%; + } } - &.read-only { - border: none; - } - .description-data { - @include multiline-ellipsis($lineCount: 3, $bgColor: $tlv-gray); - } } .vendor-description-readonly { - @extend .flex; - @extend .body-1; - justify-content: space-between; - margin-top: 10px; - overflow: hidden; - border: none; - height: 49px; - margin-bottom: 15px; - padding: 6px; - position: relative; - left:-6px; + margin-top: 16px; } + .description-data { + @include multiline-ellipsis($lineCount: 3, $bgColor: $tlv-gray); + flex: initial; + } + .vendor-description-edit { @extend .flex; flex-direction: column; @@ -84,6 +111,7 @@ width: 101%; textarea { padding-left: 6px; + resize: vertical; } .buttons-row { @extend .flex; @@ -133,12 +161,12 @@ margin-left: 20px; justify-content: space-between; border: 1px solid $tlv-light-gray; - @include border-shadow(); + @include overview-tile-shadow(); background-color: $tlv-gray; .summary-count-item { @extend .flex; - @extend .heading-5-semibold; - padding-top: 5px; + @extend .heading-5-semibold; + align-items: center; padding-left: 45px; padding-right: 45px; border-bottom: 1px solid $tlv-light-gray; @@ -147,20 +175,23 @@ flex-grow: 2; margin-left: 5px; } - .add-button { - cursor: pointer; - font-size: larger; - background-image: url($vlm-summary-plus); - background-size: 20px; - background-repeat: no-repeat; - width:20px; - height:20px; - margin-top: 3px; - margin-left: auto; - &:hover { - cursor: pointer; - background-image: url($vlm-summary-plus-blue); - } + .summary-name-and-count { + width: 100%; + } + .svg-icon-wrapper { + + .svg-icon { + &.__plusCircle { + width: 20px; + height: 20px; + margin-top: 3px; + margin-left: auto; + fill: $dark-gray; + &:hover { + fill: $blue; + } + } + } } .summary-name-and-count { @@ -184,9 +215,10 @@ } } } - .vlm-list-tab-panel - { - @extend .flex; + .vlm-list-tab-panel { + + @extend .flex; + margin-bottom: 7px; .section-title { flex: 1; } @@ -200,6 +232,7 @@ margin-left:10px; cursor: pointer; } + /** .vlm-list-icon { background-size: 32px; background-repeat: no-repeat; @@ -216,7 +249,7 @@ background-image: url($vlm-summary-orphans-blue); } } - + **/ } } @@ -236,81 +269,33 @@ } .vlm-list-view { .vlm-list { - .vlm-list-item { - @include border-shadow(); - .list-item-icon-col { - display: flex; - flex-direction: column; - justify-content: center; - margin-left: 5px; - } - &.vlm-list-item-la { - margin-left: 0; - .list-item-icon-col { - padding-left: 22px; + .vlm-list-item.orphan-list-item { + @include overview-tile-shadow(); + margin-left: 0; - } - .la-icon { - background-color: $gray; - border-color: $gray; - } - .list-item-section { - &:first-child { - background-color: gray; - } - } - } - &.vlm-list-item-fg { - cursor: default; - margin-left: 0; - border-left: 10px solid $gray; - .list-item-arrow-col { - margin-left: 0px; - } - .list-item-icon-col { - padding-left: 22px; - } - .fg-icon { - background-color: $gray; - border-color: $gray; - } - } - &.vlm-list-item-ep { - margin-left: 0; - border-left: 10px solid $gray; - .list-item-icon-col { - padding-left: 22px; - } - .ep-icon { - background-color: $gray; - border-color: $gray; - } - .list-item-section { - &:first-child { - display: flex; - color: $white; - min-width: 34px; - } - } - } - &.vlm-list-item-lkg { - margin-left: 0; - border-left: 10px solid $gray; - .list-item-icon-col { - padding-left: 22px; - } - .lkg-icon { - background-color: $gray; - border-color: $gray; - } - .list-item-section { - &:first-child { - display: flex; - color: $white; - min-width: 34px; - } - } - } + .vlm-list-item-title { + + .item-name { + color: $dark-gray; + } + } + .list-item-icon-col { + .overview-list-icon { + background-color: $gray; + border-color: $gray; + } + } + .list-item-section { + &:first-child { + display: flex; + color: $white; + min-width: $arrow-element-width; + } + &.list-item-icon-col { + margin-left: 65px; + margin-right: 65px - $list-item-padding; + } + } } } } @@ -328,18 +313,32 @@ @extend .flex; border: 1px solid $tlv-light-gray; margin-bottom: 0px; - cursor: pointer; - .list-item-icon-col { - display: flex; - flex-direction: column; - justify-content: center; + .clickable { + cursor: pointer; + } - } .list-item-section { - padding: 10px; + .count-value { + @extend .body-3; + } + &.list-item-icon-col { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 0; + margin-right: 40px - $list-item-padding; + margin-left: $icon-margin; + .overview-list-icon { + @extend .body-2; + } + } + padding: 15px; + &.list-item-arrow-col { + padding: 0; + } .children-count { @extend .body-1; - line-height: 20px; color: $gray; padding-left: 5px; .count-value { @@ -347,9 +346,18 @@ } } .additional-data { - padding-left: 50px; + padding-left: 60px; + display: flex; + flex-direction: column; + justify-content: space-between; + *:last-child { + margin-top: 20px; + } + *:only-child { + margin-top: 0; + } .additional-data-name { - @extend .body-1-semibold; + @extend .body-2-semibold; } } .additional-data-col-border { @@ -359,128 +367,140 @@ } } .list-item-additional-data-col { - @extend .body-1; + @extend .body-2; @extend .flex; - align-items: center; - flex: 0.8; + align-items: stretch; + flex: 0.8; + margin-left: 30px; + padding-top: 17px; + padding-bottom: 11px; } .arrow-icon { - align-self: center; + align-self: center; + } .vlm-item-info { flex: 1; } .vlm-list-item-title { - @extend .flex; + @extend .flex; + align-items: baseline; .item-name { @extend .heading-5-semibold; flex: 0 1 auto; - margin-bottom: 4px; + margin-bottom: 4px; + text-transform: uppercase; } } .vlm-list-item-description { @extend .body-1; - overflow: hidden; + @include multiline-ellipsis($lineCount: 3, $bgColor: $tlv-gray); max-height: 38px; } &.vlm-list-item-la { margin-top: 10px; - border-left: 10px solid $dark-blue; .la-icon { - @include create-circle($circle-icon-size,$dark-blue,'LA'); + @include create-circle($circle-icon-size,$la-color,'LA'); color: $white; - } + } + .vlm-list-item-title { + .item-name { + color: $la-color; + } + } .list-item-section { &:first-child { display: flex; - color: $dark-blue; - min-width: 34px; + color: $la-color; + min-width: $arrow-element-width; } } .list-item-arrow-col { flex: 0.01; - margin-left: 14px; + margin-left: $arrow-margin; + justify-content: center; } - .list-item-icon-col { - padding-left: 14px; - padding-right: 30px; - align-items: center; - } - } &.vlm-list-item-fg { - border-left: 10px solid $blue; - margin-left: 20px; - margin-top: 10px; + margin-left: $list-indentation; + margin-top: 10px; + .fg-pipeline-separator { + color: $dark-gray; + padding: 0 5px; + } + .list-item-icon-col { + margin-left: 29px; + } .fg-icon { - @include create-circle($circle-icon-size,$blue,'FG'); + @include create-circle($circle-icon-size,$fg-color,'FG'); color: $white; - } + } + + .vlm-list-item-title { + .item-name { + color: $fg-color; + } + } .list-item-section { &:first-child { display: flex; - color: $blue; - min-width: 34px; + color: $fg-color; + min-width: $arrow-element-width; } } .list-item-arrow-col { flex: 0.01; - margin-left: 26px; - } - .list-item-icon-col { - padding-left: 22px; - padding-right: 30px; - align-items: center; + margin-left: $arrow-margin - $list-indentation; + padding-left: $list-indentation; } } &.vlm-list-item-ep { - margin-left: 40px; + margin-left: $list-indentation * 2; margin-top: 10px; - border-left: 10px solid $light-blue; cursor: default; .ep-icon { - @include create-circle($circle-icon-size,$light-blue,'EP'); + @include create-circle($circle-icon-size,$lkg-ep-color,'EP'); color: $white; - } - .list-item-icon-col { - padding-left: 72px; - padding-right: 30px; - align-items: center; - } + } + .vlm-list-item-title { + .item-name { + color: $lkg-ep-color; + } + } .list-item-section { &:first-child { display: none; - } - } - .list-item-additional-data-col { - margin-right: 20px; + } + &.list-item-icon-col { + margin-left: 52px; + } } + } &.vlm-list-item-lkg { margin-top: 10px; - margin-left: 40px; - border-left: 10px solid $light-blue; + margin-left: $list-indentation * 2; cursor: default; .lkg-icon { - @include create-circle($circle-icon-size,$light-blue,'KG'); + @include create-circle($circle-icon-size,$lkg-ep-color,'KG'); color: $white; - } - .list-item-icon-col { - padding-left: 72px; - padding-right: 30px; - align-items: center; - } + } + .vlm-list-item-title { + .item-name { + color: $lkg-ep-color; + } + } .list-item-section { &:first-child { display: none; - } + } + &.list-item-icon-col { + margin-left: 52px; + } } - .list-item-additional-data-col { - margin-right: 20px; - } } } diff --git a/openecomp-ui/resources/scss/modules/_softwareProductAttachmentPage.scss b/openecomp-ui/resources/scss/modules/_softwareProductAttachmentPage.scss index bab2872691..0e092dd8bb 100644 --- a/openecomp-ui/resources/scss/modules/_softwareProductAttachmentPage.scss +++ b/openecomp-ui/resources/scss/modules/_softwareProductAttachmentPage.scss @@ -22,20 +22,7 @@ right: 40px; top: 15px; display: flex; - .icon-label { - color: $dark-gray; - } - .go-to-overview-icon { - .icon-label { - color: $blue; - } - &.disabled { - .icon-label { - color: $gray; - } - } - } .icon-component { margin-right: 30px; @@ -104,6 +91,11 @@ z-index: 1; padding-right: 20px; .counter { + .svg-icon { + &.__exclamationTriangleLine { + fill :$orange; + } + } display: flex; &:first-child { margin-right: 20px; @@ -145,8 +137,8 @@ justify-content: space-between; height: 40px; align-items: center; - .svg-icon-wrapper.chevronDown, .svg-icon-wrapper.chevronUp { - .svg-icon { + .svg-icon { + &.__chevronUp, &.__chevronDown { height: 10px; width: 10px; } @@ -224,6 +216,13 @@ .error-item { display: flex; margin: 10px 0; + .large { + .svg-icon { + width: 20px; + height: 20px; + fill: $orange; + } + } .error-item-file-type { margin-left: 15px; } @@ -231,6 +230,9 @@ @extend .body-1-semibold; margin-right: 5px; } + + + } } diff --git a/openecomp-ui/resources/scss/modules/_softwareProductComponentImage.scss b/openecomp-ui/resources/scss/modules/_softwareProductComponentImage.scss index 068404fcb6..dad837f356 100644 --- a/openecomp-ui/resources/scss/modules/_softwareProductComponentImage.scss +++ b/openecomp-ui/resources/scss/modules/_softwareProductComponentImage.scss @@ -16,6 +16,12 @@ } } +.vsp-components-image-editor { + .note-text { + color: $red; + } +} + .image-modal-edit, .image-modal-new { .modal-body { padding: 0; diff --git a/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss b/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss index 56860101b2..9b0375d24a 100644 --- a/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss +++ b/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss @@ -28,21 +28,19 @@ .missing-license { display: flex; align-items: baseline; - .svg-icon-wrapper.exclamationTriangleFull .svg-icon{ - fill: $orange; + .svg-icon{ + margin-right: 7px; + margin-left: 3px; + &.__exclamationTriangleFull { + fill: $orange; + width: 17px; + height: 17px; + } } .warning-text { position: relative; top: -2px; } - .svg-icon-wrapper { - margin-right: 7px; - margin-left: 3px; - .svg-icon { - height: 17px; - width: 17px; - } - } } } .name { diff --git a/openecomp-ui/resources/scss/modules/_softwareproductComponentLoadBalancing.scss b/openecomp-ui/resources/scss/modules/_softwareproductComponentLoadBalancing.scss index ceae4a00de..d1f3f488bd 100644 --- a/openecomp-ui/resources/scss/modules/_softwareproductComponentLoadBalancing.scss +++ b/openecomp-ui/resources/scss/modules/_softwareproductComponentLoadBalancing.scss @@ -1,13 +1,7 @@ .vsp-components-load-balancing { .svg-icon-wrapper { position: relative; - top: -3px; - &.chevronUp, &.chevronDown { - .svg-icon { - width: 10px; - height: 10px; - } - } + top: -4px; } .halb-data { diff --git a/openecomp-ui/resources/scss/modules/_vspComponentMonitoring.scss b/openecomp-ui/resources/scss/modules/_vspComponentMonitoring.scss index 4bcf7940eb..bda8512f36 100644 --- a/openecomp-ui/resources/scss/modules/_vspComponentMonitoring.scss +++ b/openecomp-ui/resources/scss/modules/_vspComponentMonitoring.scss @@ -1,6 +1,6 @@ .vsp-component-monitoring { - .snmp-dropzone { + .dropzone { .section-title { padding-bottom: 20px; } @@ -22,7 +22,7 @@ } .drag-text { color: $blue; - @extend .body-1-semibold; + @extend .body-1-semibold } .or-text { margin-top: 10px; @@ -30,6 +30,20 @@ } } + .monitoring-file { + display: flex; + .filename { + opacity: 1; + width: auto; + border-right-style: none; + } + .delete { + display: flex; + width: 30px; + justify-content: center; + align-items: center; + } + } } .delete-button { diff --git a/openecomp-ui/resources/scss/modules/_vspHeatSetup.scss b/openecomp-ui/resources/scss/modules/_vspHeatSetup.scss index 75d96b4c85..bd216c794b 100644 --- a/openecomp-ui/resources/scss/modules/_vspHeatSetup.scss +++ b/openecomp-ui/resources/scss/modules/_vspHeatSetup.scss @@ -24,10 +24,12 @@ position: relative; top: -2px; } + .modules-list-wrapper { - padding-bottom: 20px; - margin-bottom: 20px; - border-bottom: 1px solid $tlv-light-gray; + &.modules-list-wrapper-divider { + border-bottom: 1px solid $tlv-light-gray; + } + ul { .undefined-dragging { opacity: 0.5; @@ -36,7 +38,7 @@ display: flex; justify-content: space-between; flex-wrap: wrap; - + padding-bottom: 3px; .Select-value-label { @include ellipsis(85%); } @@ -55,24 +57,6 @@ } } - .modules-list-controllers { - text-align: right; - .btn-link { - &[disabled] { - color: $gray; - } - @extend .body-1; - color: $blue; - &:last-child { - padding-right: 0; - } - &:hover { - color: $dark-blue; - text-decoration: none; - } - } - } - .modules-list-item-controllers { display: flex; justify-content: space-between; @@ -82,16 +66,15 @@ min-width: 0; } - .svg-icon-wrapper.trashO { - .svg-icon { - fill: $dark-gray; - height: 18px; - width: 18px; - &:hover { - fill: $black; + .svg-icon { + &.__trashO { + fill: $dark-gray; + &:hover { + fill: $black; + } } - } } + .module-title-by-type { @extend .heading-5-semibold; margin-right: 3px; @@ -100,15 +83,15 @@ display: flex; align-items: center; - .svg-icon-wrapper.pencil { - .svg-icon { - height: 15px; - width: 15px; + .svg-icon { + &.__pencil { + margin-left: 3px; + opacity: 0; } - margin-left: 3px; - opacity: 0; } + + .filename-text { @extend .heading-5-semibold; @@ -144,14 +127,16 @@ border-color: transparent; } } - .svg-icon-wrapper.pencil { - margin-left: 10px; - opacity: 1; - .svg-icon { - stroke: $dark-gray; - &:hover { - stroke: $black; - } + .svg-icon { + &.__pencil { + margin-left: 10px; + opacity: 1; + .svg-icon { + stroke: $dark-gray; + &:hover { + stroke: $black; + } + } } } } @@ -173,23 +158,8 @@ } } .add-or-delete-volumes { - .svg-icon-wrapper { - margin-right: 8px; - .svg-icon { - height: 10px; - width: 10px; - fill: $blue; - } - } - cursor: pointer; - color: $blue; + margin-right: 8px; margin-bottom: 11px; - &:hover { - color: $dark-blue; - .svg-icon { - fill: $dark-blue; - } - } } &:before { content: "\00B7\00B7\00B7\00B7\00B7\00B7"; @@ -208,7 +178,9 @@ .artifact-files { @include modules-and-artifacts-list-items; - margin-top: 20px; + &.with-list-items { + margin-top: 10px; + } &.nested { .nested-list { @@ -238,21 +210,20 @@ display: flex; } - .add-all-unassigned { - @extend .body-1; - margin-bottom: 0; - color: $blue; - cursor: pointer; - &:hover { - color: $dark-blue; - } - } } } } + .modules-list-header { + height: 30px; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-end; + align-items: baseline; + } + .unassigned-files { - margin-top: 30px; border: 1px solid $light-gray; background-color: $white; height: 250px; @@ -283,26 +254,12 @@ @extend .heading-4; margin-bottom: 10px; } - .link { - color: $blue; - cursor: pointer; - display: flex; - align-items: center; + .svg-icon-wrapper { margin-bottom: 10px; - .svg-icon-wrapper.angleRight { - .svg-icon { - height: 10px; - width: 10px; - margin-left: 7px; - fill: $blue; - } - } - &:hover { - color: $dark-blue; - .svg-icon-wrapper.angleRight { - .svg-icon { - fill: $dark-blue; - } + .svg-icon { + &.__angleRight { + width: 10px; + height: 10px; } } } diff --git a/openecomp-ui/resources/scss/modules/onboardingCatalog/_catalogTile.scss b/openecomp-ui/resources/scss/modules/onboardingCatalog/_catalogTile.scss index 07f86aba2a..612ec495cd 100644 --- a/openecomp-ui/resources/scss/modules/onboardingCatalog/_catalogTile.scss +++ b/openecomp-ui/resources/scss/modules/onboardingCatalog/_catalogTile.scss @@ -48,6 +48,9 @@ align-self: baseline; color: $gray; line-height: 10px; + &:first-letter { + text-transform: capitalize; + } @include ellipsis(auto, inline-block, 178px); } @@ -56,7 +59,10 @@ @extend .heading-5-semibold; color: $black; @include ellipsis(auto, inline-block, 175px); - line-height: inherit; + line-height: inherit; + &:first-letter { + text-transform: capitalize; + } } } .catalog-tile-icon { @@ -73,36 +79,22 @@ width: 58px; margin-left: 122px; background-repeat: no-repeat; - .svg-icon-wrapper { - &.vendor { - .svg-icon { + .svg-icon { + &.__vendor { fill: $dark-gray; - width: 53px; - height: 47px; &:hover { fill: $dark-gray; } - } margin-top: 22px; - } - &.vsp { - .svg-icon { + &.__vsp { fill: $light-blue; - width: 60px; - height: 40px; } margin-top: 18px; margin-left: 3px; - } - &.vlm { + &.__vlm { margin-top: 18px; - .svg-icon { fill: $purple; - - width: 45px; - height: 53px; - } } } } @@ -115,13 +107,11 @@ margin-top: 2px; padding-bottom: 3px; @extend .body-2-semibold; - .svg-icon-wrapper { - &.plus { - .svg-icon { - height: 9px; - width: 9px; + .svg-icon { + &.__plus { + width: 20px; + height: 20px; fill: $blue; - } } } .catalog-tile-item-details { @@ -130,6 +120,13 @@ .catalog-tile-add-new-vsp { color: $blue; + .svg-icon { + &.__plus { + width: 20px; + height: 20px; + + } + } margin-left: 40px; } .catalog-tile-locking-user-name { @@ -137,18 +134,12 @@ @include ellipsis(auto, inline-block, 180px); } .catalog-tile-check-in-status { - .svg-icon-wrapper { - &.locked { - .svg-icon { - width: 11px; + .svg-icon { + &.__locked { fill: $gray; - } } - &.unlocked { - .svg-icon { - width: 11px; + &.__unlocked { fill: $gray; - } } } } diff --git a/openecomp-ui/resources/scss/modules/onboardingCatalog/_createItemTile.scss b/openecomp-ui/resources/scss/modules/onboardingCatalog/_createItemTile.scss index aa3cf04c4f..d17dcdd685 100644 --- a/openecomp-ui/resources/scss/modules/onboardingCatalog/_createItemTile.scss +++ b/openecomp-ui/resources/scss/modules/onboardingCatalog/_createItemTile.scss @@ -37,12 +37,14 @@ } } .create-item-plus-icon{ - width: 19px; - height: 19px; - margin: -5px 12px 0 0; - .svg-icon.plus { - height: 19px; - width: 19px; + margin: -5px 12px 0 0; + height: 19px; + width: 19px; + .svg-icon { + &.__plus { + width: 19px; + height: 19px; + } } } &.vlm-type { diff --git a/openecomp-ui/resources/scss/modules/onboardingCatalog/_vendorTile.scss b/openecomp-ui/resources/scss/modules/onboardingCatalog/_vendorTile.scss index 5972bb2d67..d490fd6e1c 100644 --- a/openecomp-ui/resources/scss/modules/onboardingCatalog/_vendorTile.scss +++ b/openecomp-ui/resources/scss/modules/onboardingCatalog/_vendorTile.scss @@ -24,7 +24,10 @@ .catalog-tile-top { .catalog-tile-item-name { margin-top: 21px; - line-height: inherit; + line-height: inherit; + &:first-letter { + text-transform: capitalize; + } } .catalog-tile-vsp-count { @extend .body-3-semibold; diff --git a/openecomp-ui/resources/scss/onboarding.scss b/openecomp-ui/resources/scss/onboarding.scss index d7d4586354..94dc223be3 100644 --- a/openecomp-ui/resources/scss/onboarding.scss +++ b/openecomp-ui/resources/scss/onboarding.scss @@ -12,53 +12,73 @@ div[data-reactroot].customized-date-picker-calendar { @import "common/variables"; @import "common/typography"; - border-radius: 2px; border-color: $light-gray; margin-top: -8px; + color: $black; .react-datepicker__triangle { margin-top: 0px; } .react-datepicker__navigation--previous { - border: none; - width: 15px; - height: 15px; + border: none; + width: 12px; + height: 12px; margin-top: 2px; - content: url(../images/angle-left.svg); + content: url(../../node_modules/sdc-ui/assets/icons/angleLeft.svg); } .react-datepicker__navigation--next { - border: none; - width: 15px; - height: 15px; - margin-top: 2px; - content: url(../images/angle-right.svg); + border: none; + width: 12px; + height: 12px; + margin-top: 2px; + content: url(../../node_modules/sdc-ui/assets/icons/angleRight.svg); } - .react-datepicker__month-container { + .react-datepicker__month-container { .react-datepicker__header { background-color: $background-gray; border-bottom: none; .react-datepicker__current-month { - @extend .heading-5; + @extend .body-1-semibold; background-color: $background-gray; - margin-bottom: 10px; + margin-bottom: 10px; } .react-datepicker__day-names { - @extend .heading-5; + @extend .body-1; background-color: $white; } } - .react-datepicker__day--selected { - @extend .heading-5; - border-radius: 20px; + .react-datepicker__day--selected, .react-datepicker__day--keyboard-selected { + @extend .body-1; background-color: $blue; - &:hover { - background-color: $dark-blue; - } + color: $white; } .react-datepicker__day { - @extend .heading-5; - &:hover { - border-radius: 20px; - } + border-radius: 0px; + margin: 0; + flex: 1; + @extend .body-1; + } + + .react-datepicker__day--in-range, .react-datepicker__day--in-selecting-range { + background-color: $tlv-hover; + color: $black; + &.react-datepicker__day--selected, &.react-datepicker__day--keyboard-selected, &.react-datepicker__day--range-start, &.react-datepicker__day--range-end { + background-color: $blue; + color: $white; + } + &.react-datepicker__day--selecting-range-start, &.react-datepicker__day--selecting-range-end { + background-color: lighten($blue, 40%); + color: $black; + } + &.react-datepicker__day--selecting-range-start.react-datepicker__day--range-start, + &.react-datepicker__day--selecting-range-end.react-datepicker__day--range-end, + &.react-datepicker__day--selecting-range-start.react-datepicker__day--keyboard-selected { + background-color: $blue; + color: $white; + } + } + + .react-datepicker__week { + display: flex; } } } @@ -69,7 +89,7 @@ div[data-reactroot].tooltip { @import "common/typography"; &.in { - opacity: 1; + opacity: 1; } &.validation-error-message { &.bottom { @@ -94,28 +114,33 @@ div[data-reactroot].tooltip { } } &.bottom { - .tooltip-arrow { - border-bottom-color: $dark-gray !important; - } + .tooltip-arrow { + border-bottom-color: $dark-gray !important; + } } &.top { - .tooltip-arrow { - border-top-color: $dark-gray !important; - } + .tooltip-arrow { + border-top-color: $dark-gray !important; + } } .tooltip-inner { - max-width: 100%; - background-color: $dark-gray; + @extend .body-1; + max-width: 100%; + background-color: $dark-gray; + padding: 6px 9px; + &:first-letter { + text-transform: capitalize; + } } // activity log tooltip &.activity-log-message-tooltip { - @include base-font-regular; - font-size: $body-font-2; - .message-block { - text-align: left; - padding: 3px 12px; - } + @include base-font-regular; + font-size: $body-font-2; + .message-block { + text-align: left; + padding: 3px 12px; + } } //dependency table error tooltip &.select-action-table-error-tooltip{ @@ -136,4 +161,11 @@ div[data-reactroot].tooltip { font-size: $body-font-2; } } + + // for read-only mode, vendor description in VLM overview + &.vendor-description-tooltip { + .tooltip-inner { + max-width: 600px; + } + } } diff --git a/openecomp-ui/runLocalFE.cmd b/openecomp-ui/runLocalFE.cmd deleted file mode 100644 index ee718f2a0b..0000000000 --- a/openecomp-ui/runLocalFE.cmd +++ /dev/null @@ -1,38 +0,0 @@ -@REM /*! -@REM * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. -@REM * -@REM * Licensed under the Apache License, Version 2.0 (the "License"); -@REM * you may not use this file except in compliance with the License. -@REM * You may obtain a copy of the License at -@REM * -@REM * http://www.apache.org/licenses/LICENSE-2.0 -@REM * -@REM * Unless required by applicable law or agreed to in writing, software -@REM * distributed under the License is distributed on an "AS IS" BASIS, -@REM * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -@REM * or implied. See the License for the specific language governing -@REM * permissions and limitations under the License. -@REM */ - -@echo off - -SETLOCAL - -set uiDir=%cd% -set currentDir=%cd% -if not ("%1" == "") set uiDir=%1 - -echo check npm version: -call npm -version -if errorlevel 1 ( - echo install node with npm from https://nodejs.org/en/download/ - goto done -) -echo npm is installed -echo one more check... -call npm list prompt -if errorlevel 1 ( - npm install prompt -) -echo ready to run -call node runLocalFE.js diff --git a/openecomp-ui/runLocalFE.js b/openecomp-ui/runLocalFE.js deleted file mode 100644 index 1205aa48f9..0000000000 --- a/openecomp-ui/runLocalFE.js +++ /dev/null @@ -1,104 +0,0 @@ -/*! - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this 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. - */ - -var exec = require('child_process'); -var prompt = require('prompt'); -var fs = require('fs'); - - -function runNpm(target, dir) { - console.log('\n---> npm ' + target); - let options = {stdio:[0,1,2]}; - if (dir) options.cwd = dir; - exec.execSync("npm " + target,options); -} - -function npmInstallAll() { - setNpmconfig(); - if (!fs.existsSync('../dox-sequence-diagram-ui/node_modules')) { - console.log('--> first time installing dox-sequence-diagram-ui'); - runNpm('install', '../dox-sequence-diagram-ui'); - }; - runNpm('install'); - // just to make sure restful js is installed properly - runNpm('install jquery', 'node_modules/restful-js'); -} - -function getDevConfig() { - var content=fs.readFileSync('./devConfig.json'); - var data=JSON.parse(content); - console.log('Current ATT server is set to: ' + data.proxyATTTarget); - if (!data.proxyTarget) { - console.log('Current onboarding server defaults to the ATT server'); - } else { - console.log('Current onboarding server set to: ' + data.proxyTarget); - } - return data; -} - -function setNpmconfig() { - exec.execSync("npm config set proxy http://genproxy.amdocs.com:8080"); - exec.execSync("npm config set https_proxy http://genproxy.amdocs.com:8080"); -} - -// getting the run details before starting to work -prompt.start(); -prompt.get([{ - name:'runType', - type:'number', - default:1, - description: 'Choose run: 1-test and build, 2- run frontend server ' - }], function (err, result) { - if (result.runType === 2) { - console.log('--> Reading the configuration for the local server'); - if (!fs.existsSync('./devConfig.json')) { - console.log('First time - setting up the devConfig.json file'); - fs.writeFileSync('./devConfig.json', fs.readFileSync('./devConfig.defaults.json')); - } - let data = getDevConfig(); - let attProxyField = { - name:'attProxyTarget', - description:'ATT server' - }; - let proxyField = { - name:'proxyTarget', - description:'onboarding server, \'null\' to reset' - }; - if (data.proxyATTTarget) attProxyField.default = data.proxyATTTarget; - if (data.proxyTarget) proxyField.default = data.proxyTarget; - prompt.get([ attProxyField, proxyField], function (err,result) { - data.proxyATTTarget = result.attProxyTarget; - if(result.proxyTarget) { - if (result.proxyTarget === 'null') { - if (data.proxyTarget) delete data.proxyTarget; - } else { - data.proxyTarget = result.proxyTarget; - } - } - fs.writeFileSync('./devConfig.json', JSON.stringify(data, null, 2)); - getDevConfig(); - console.log('FE server will be answering on: http://localhost:9000/sdc1/proxy-designer1#/onboardVendor'); - npmInstallAll(); - runNpm("start"); - } - ); - } else { - npmInstallAll(); - runNpm("run build"); - runNpm("run test"); - } -}); - diff --git a/openecomp-ui/src/index.html b/openecomp-ui/src/index.html index d4fab4dcb2..6a9265c984 100644 --- a/openecomp-ui/src/index.html +++ b/openecomp-ui/src/index.html @@ -11,4 +11,4 @@ </body> </html> -npm + diff --git a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx index 5fe592a663..1488c6c0bb 100644 --- a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx +++ b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx @@ -17,7 +17,6 @@ import React, {Component} from 'react'; import ListGroupItem from 'react-bootstrap/lib/ListGroupItem.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; -import Icon from 'nfvo-components/icon/Icon.jsx'; import {Collapse} from 'react-bootstrap'; /** * parsing and showing the following Java Response object @@ -145,8 +144,7 @@ class ErrorBlock extends React.Component { const ErrorHeader = ({errorType, collapsed, onClick}) => { return( <div onClick={onClick} className='error-block-header'> - <SVGIcon iconClassName={collapsed ? '' : 'right' } name='chevronDown'/> - {errorType} + <SVGIcon iconClassName={collapsed ? '' : 'collapse-right' } name='chevronDown' label={errorType} labelPosition='right'/> </div> ); }; @@ -154,7 +152,7 @@ const ErrorHeader = ({errorType, collapsed, onClick}) => { const ErrorMessage = ({error, warning}) => { return ( <ListGroupItem className='error-code-list-item'> - <Icon image={warning ? 'warning' : 'error'} label={error}/> + <SVGIcon name={warning ? 'warning' : 'error'} label={error} labelPosition='right' color={warning ? 'warning' : 'negative'} labelClassName='icon-label'/> </ListGroupItem> ); }; diff --git a/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx b/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx index cd39376bda..f4673c1c93 100644 --- a/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx +++ b/openecomp-ui/src/nfvo-components/datepicker/Datepicker.jsx @@ -17,10 +17,10 @@ class CustomInput extends React.Component { const text = date ? date : placeholderText; const textStyle = date ? '' : 'placeholder'; return ( - <div ref={inputRef} className='datepicker-custom-input'> - <div onClick={onClick} className={`datepicker-text ${textStyle}`}>{text}</div> - <div onClick={onClear} className='clear-input'/> - <SVGIcon onClick={onClick} name='calendar'/> + <div onClick={onClick} ref={inputRef} className='datepicker-custom-input'> + <div className={`datepicker-text ${textStyle}`}>{text}</div> + {date && <SVGIcon onClick={e => {e.stopPropagation(); onClear();}} name='close' className='clear-input'/>} + <SVGIcon name='calendar'/> </div> ); } diff --git a/openecomp-ui/src/nfvo-components/fileupload/DraggableUploadFileBox.jsx b/openecomp-ui/src/nfvo-components/fileupload/DraggableUploadFileBox.jsx new file mode 100644 index 0000000000..629b9449a2 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/fileupload/DraggableUploadFileBox.jsx @@ -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. + */ +/** + * The HTML structure here is aligned with bootstrap HTML structure for form elements. + * In this way we have proper styling and it is aligned with other form elements on screen. + * + * Select and MultiSelect options: + * + * label - the label to be shown which paired with the input + * + * all other "react-select" props - as documented on + * http://jedwatson.github.io/react-select/ + * or + * https://github.com/JedWatson/react-select + */ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Button from 'sdc-ui/lib/react/Button.js'; + +class DraggableUploadFileBox extends Component { + render() { + let {className, onClick, dataTestId, isReadOnlyMode} = this.props; + return ( + <div + className={`${className}${isReadOnlyMode ? ' disabled' : ''}`}> + <div className={`${'drag-text'}${isReadOnlyMode ? ' disabled' : ''}`}>{i18n('Drag & drop for upload')}</div> + <div className='or-text'>{i18n('or')}</div> + <Button type='button' data-test-id={dataTestId} btnType='outline' onClick={onClick} disabled={isReadOnlyMode === true}>{i18n('Select File')}</Button> + </div> + ); + } +} +export default DraggableUploadFileBox; diff --git a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx index 7bbafa3696..23af72a26a 100644 --- a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx @@ -41,6 +41,7 @@ class ToggleInput extends React.Component { render() { let {label, disabled} = this.props; let checked = this.status() === 'on'; + //TODO check onclick return ( <div className='toggle-input-wrapper form-group' onClick={!disabled && this.click}> <div className='toggle-input-label'>{label}</div> diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx index 6e54254eb0..9b11d27c2b 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx @@ -64,7 +64,8 @@ class InputOptions extends React.Component { const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; let currentMultiSelectedEnum = []; let currentSelectedEnum = ''; - let {otherInputDisabled} = this.state; + let otherInputDisabled = (isMultiSelect && (multiSelectedEnum === undefined || multiSelectedEnum.length === 0 || multiSelectedEnum[0] !== other.OTHER)) + || (!isMultiSelect && (selectedEnum === undefined || selectedEnum !== other.OTHER)); if (isMultiSelect) { currentMultiSelectedEnum = multiSelectedEnum; if(!otherInputDisabled) { diff --git a/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx index 95144b1468..6f33536b04 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx @@ -15,7 +15,7 @@ */ import React from 'react'; import ReactDOM from 'react-dom'; -import {default as BTabs} from 'react-bootstrap/lib/Tabs.js'; +import {default as SDCTabs} from 'sdc-ui/lib/react/Tabs.js'; import Overlay from 'react-bootstrap/lib/Overlay.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; @@ -33,15 +33,15 @@ class Tabs extends React.Component { return React.cloneElement( element, { - key: element.props.eventKey, - tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab' + key: element.props.tabId, + className: invalidTabs.indexOf(element.props.tabId) > -1 ? 'invalid-tab' : 'valid-tab' } ); } showTabsError() { const {invalidTabs} = this.props; - const showError = ((invalidTabs.length === 1 && invalidTabs[0] !== this.props.activeKey) || (invalidTabs.length > 1)); + const showError = ((invalidTabs.length === 1 && invalidTabs[0] !== this.props.activeTab) || (invalidTabs.length > 1)); return showError; } @@ -50,21 +50,20 @@ class Tabs extends React.Component { let {invalidTabs, ...tabProps} = this.props; return ( <div> - <BTabs {...tabProps} ref='tabsList' id='tabsList' > + <SDCTabs {...tabProps} ref='tabsList' id='tabsList' > {this.props.children.map(element => this.cloneTab(element))} - </BTabs> + </SDCTabs> <Overlay animation={false} show={this.showTabsError()} placement='bottom' - containerPadding={50} target={() => { - let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); + let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.sdc-tab-active):nth-of-type(n)'); return target && target.offsetParent ? target : undefined; } } container={() => { - let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); + let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.sdc-tab-active):nth-of-type(n)'); return target && target.offsetParent ? target.offsetParent : this; }}> <Tooltip diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx index 8d7c63f567..bed304b756 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx @@ -39,7 +39,7 @@ class ListEditorItem extends React.Component { </div> {(onEdit || onDelete) && <div className='list-editor-item-view-controller'> {onEdit && <SVGIcon name='sliders' onClick={() => this.onClickedItem(onEdit)}/>} - {onDelete && isAbilityToDelete && <SVGIcon name='trashO' onClick={() => this.onClickedItem(onDelete)}/>} + {onDelete && isAbilityToDelete && <SVGIcon name='trashO' data-test-id='delete-list-item' onClick={() => this.onClickedItem(onDelete)}/>} </div>} </div> ); diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx index cc805e9ada..df7d69b4ff 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx @@ -14,6 +14,7 @@ * permissions and limitations under the License. */ import React from 'react'; +import Button from 'sdc-ui/lib/react/Button.js'; import classnames from 'classnames'; import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; @@ -21,11 +22,9 @@ const ListEditorHeader = ({onAdd, isReadOnlyMode, title, plusButtonTitle}) => { return ( <div className='list-editor-view-header'> {title && <div className='list-editor-view-title'>{title}</div>} - <div className={`list-editor-view-add-controller${isReadOnlyMode ? ' disabled' : ''}`}> + <div> { onAdd && - <div className='list-editor-view-add-title' data-test-id='add-button' onClick={onAdd}> - <span>{`+ ${plusButtonTitle}`}</span> - </div> + <Button data-test-id='add-button' iconName='plusThin' btnType='link' onClick={onAdd} disabled={isReadOnlyMode === true}>{plusButtonTitle}</Button> } </div> </div> diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModal.js b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js index 825cc609a8..e05c2ac616 100644 --- a/openecomp-ui/src/nfvo-components/modal/GlobalModal.js +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js @@ -31,21 +31,35 @@ const typeClass = { success: 'positive' }; +const type2HeaderColor = { + 'default': 'primary', + error: 'danger', + warning: 'warning', + success: 'success' +}; + -const ModalFooter = ({type, onConfirmed, onDeclined, onClose, confirmationButtonText, cancelButtonText}) => +const ModalFooter = ({type, onConfirmed, onDeclined, onClose, confirmationButtonText, cancelButtonText}) => { + let myPropsForNoConfirmed = {}; + if (onConfirmed) { + myPropsForNoConfirmed.btnType = 'outline'; + } + return ( <Modal.Footer> <div className='sdc-modal-footer'> {onConfirmed && <Button color={typeClass[type]} onClick={() => { onConfirmed(); onClose(); }}>{confirmationButtonText}</Button>} - <Button btnType='outline' color={typeClass[type]} onClick={onDeclined ? () => { + <Button {...myPropsForNoConfirmed} color={typeClass[type]} onClick={onDeclined ? () => { onDeclined(); onClose();} : () => onClose()}> {cancelButtonText} </Button> </div> - </Modal.Footer>; + </Modal.Footer> + ); +}; ModalFooter.defaultProps = { type: 'default', @@ -93,7 +107,7 @@ export class GlobalModalView extends React.Component { modalClassName, msg, onConfirmed, onDeclined, confirmationButtonText, cancelButtonText, onClose} = this.props; const ComponentToRender = modalContentComponents[modalComponentName]; return ( - <Modal show={show} bsSize={modalComponentProps && modalComponentProps.size} className={`onborading-modal ${modalClassName || ''} ${typeClass[type]}`}> + <Modal show={show} bsSize={modalComponentProps && modalComponentProps.size} className={`onborading-modal ${modalClassName || ''} ${type2HeaderColor[type]}`}> <Modal.Header> <Modal.Title>{title}</Modal.Title> </Modal.Header> diff --git a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx index 3b89137090..fb3b71f0c8 100644 --- a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx +++ b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx @@ -109,6 +109,7 @@ function NavigationMenuItem(props) { function NavigationLink(props) { const {item, activeItemId, onClick} = props; + // todo should this be button return ( <div key={'navAction_' + item.id} diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx index ecfe7df116..922db05282 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx @@ -154,7 +154,7 @@ function VCButton({name, tooltipText, isDisabled, onClick, dataTestId}) { return ( <OverlayTrigger placement='top' overlay={<Tooltip id='vc-tooltip'>{tooltipText}</Tooltip>}> <div disabled={disabled} className='action-buttons-svg'> - <SVGIcon data-test-id={dataTestId} iconClassName={disabled} onClick={onClickAction ? onClickAction : undefined} name={name}/> + <SVGIcon data-test-id={dataTestId} disabled={isDisabled} onClick={onClickAction ? onClickAction : undefined} name={name}/> </div> </OverlayTrigger> ); diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js index 9af142433c..c2548e4fb4 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js @@ -33,7 +33,7 @@ export const statusEnum = keyMirror({ export const statusBarTextMap = keyMirror({ 'Locked': 'Checked Out', - 'LockedByUser': '', + 'LockedByUser': 'Locked', 'Available': 'Checked In', 'Final': 'Submitted', 'READ ONLY': 'Locked' diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx index 6c04ad74fd..3f8dbba53a 100644 --- a/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx @@ -1,6 +1,7 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; +import Button from 'sdc-ui/lib/react/Button.js'; import uuid from 'uuid-js'; export default class SelectActionTable extends React.Component { @@ -10,13 +11,14 @@ export default class SelectActionTable extends React.Component { return ( <div className={`select-action-table-view ${isReadOnlyMode ? 'disabled' : ''}`}> <div className='select-action-table-controllers'> - {onAdd && onAddItem && <div data-test-id='select-action-table-add' onClick={onAdd}>{onAddItem}</div>} + {onAdd && onAddItem && + <Button btnType='link' disabled={isReadOnlyMode === true} color='primary' iconName='plus' data-test-id='select-action-table-add' onClick={onAdd}>{onAddItem}</Button>} <SVGIcon name='trashO' className='dummy-icon' /> </div> <div className='select-action-table'> <div className='select-action-table-headers'> {columns.map(column => <div key={uuid.create()} className='select-action-table-header'>{i18n(column)}</div>)} - {Array(numOfIcons).fill().map(() => <SVGIcon name='trashO' className='dummy-icon' />)} + {Array(numOfIcons).fill().map((e, i) => <SVGIcon name='trash-o' key={i} className='dummy-icon' />)} </div> <div className='select-action-table-body'> {children} diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx index a711b42918..a03f8441a4 100644 --- a/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx @@ -11,7 +11,7 @@ function tooltip (msg) { const IconWithOverlay = ({overlayMsg}) => ( <OverlayTrigger placement='bottom' overlay={tooltip(overlayMsg)}> - <SVGIcon name='errorCircle'/> + <SVGIcon name='errorCircle' color='negative'/> </OverlayTrigger> ); @@ -21,18 +21,18 @@ function renderErrorOrCheck({hasError, overlayMsg}) { } if (hasError) { - return overlayMsg ? <IconWithOverlay overlayMsg={overlayMsg}/> : <SVGIcon name='errorCircle'/>; + return overlayMsg ? <IconWithOverlay overlayMsg={overlayMsg}/> : <SVGIcon color='negative' name='errorCircle'/>; } - return <SVGIcon name='checkCircle'/>; + return <SVGIcon name='checkCircle' color='positive'/>; } -const SelectActionTableRow = ({children, onDelete, hasError, hasErrorIndication, overlayMsg}) => ( +const SelectActionTableRow = ({children, onDelete, hasError, hasErrorIndication, overlayMsg, showDelete}) => ( <div className='select-action-table-row-wrapper'> <div className={`select-action-table-row ${hasError ? 'has-error' : ''}`}> {children} </div> - {onDelete && <SVGIcon name='trashO' data-test-id='select-action-table-delete' onClick={onDelete} />} + {onDelete && <SVGIcon color='secondary' name='trashO' data-test-id='select-action-table-delete' onClick={onDelete} iconClassName={(showDelete) ? '' : 'hideDelete'}/>} {hasErrorIndication && renderErrorOrCheck({hasError, overlayMsg})} </div> ); diff --git a/openecomp-ui/src/nfvo-utils/Validator.js b/openecomp-ui/src/nfvo-utils/Validator.js index 8fcf24a714..1df82a2ada 100644 --- a/openecomp-ui/src/nfvo-utils/Validator.js +++ b/openecomp-ui/src/nfvo-utils/Validator.js @@ -20,7 +20,9 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; class Validator { static get globalValidationFunctions() { return { - required: value => value !== '', + required: value => { + return typeof value === 'string' ? value.replace(/\s+/g, '') !== '' : value !== ''; + }, requiredChooseOption: value => value !== '', maxLength: (value, length) => ValidatorJS.isLength(value, {max: length}), minLength: (value, length) => ValidatorJS.isLength(value, {min: length}), @@ -107,6 +109,7 @@ class Validator { } static isItemNameAlreadyExistsInList({itemId, itemName, list}) { + itemName = itemName.toLowerCase(); return list[itemName] && list[itemName] !== itemId; } } diff --git a/openecomp-ui/src/nfvo-utils/getValue.js b/openecomp-ui/src/nfvo-utils/getValue.js new file mode 100644 index 0000000000..101655bf31 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/getValue.js @@ -0,0 +1,44 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; + +function getValueFromObject(element) { + return element.choices && element.choices.length > 0 && element.choices[0] !== '' && element.choices[0] !== optionInputOther.OTHER || + element.other && element.choices[0] === optionInputOther.OTHER ? + element : undefined; +} + +function getValueFromVariable(variable) { + return variable ? variable : undefined; +} + + let getValue = element => { + return typeof element === 'object' ? getValueFromObject(element) : getValueFromVariable(element); + }; + +export function getStrValue(choiceObject) { + if (!choiceObject) { + return undefined; + } + if (choiceObject.choice && choiceObject.choice !== '' && choiceObject.choice !== optionInputOther.OTHER) { + return choiceObject.choice; + } + else if (choiceObject.other && choiceObject.choice === optionInputOther.OTHER) { + return choiceObject.other; + } +} + + export default getValue; diff --git a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx index 19ab570fd9..9abddea542 100644 --- a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx +++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx @@ -22,6 +22,7 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import LogDetails from './LogixUtil.jsx'; function ActivityLogSortableCellHeader({isHeader, data, isDes, onSort}) { + //TODO check icon sdc-ui if (isHeader) { return ( <span className='date-header' onClick={onSort}> @@ -51,7 +52,7 @@ function ActivityLogStatus({status, isHeader}) { return ( <span> <span className={`status-icon ${success}`}>{`${success ? i18n('Success') : i18n('Failure')}`}</span> - {success && <SVGIcon name='checkCircle'/>} + {success && <SVGIcon name='checkCircle' color='positive'/>} {!success && <OverlayTrigger placement='bottom' overlay={<Tooltip className='activity-log-message-tooltip' id={'activity-log-message-tooltip'}> <div className='message-block'>{message}</div> </Tooltip>}> diff --git a/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js b/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js index 51dfcf9a67..cfa675278f 100644 --- a/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js +++ b/openecomp-ui/src/sdc-app/common/helpers/ValidationHelper.js @@ -51,10 +51,11 @@ class ValidationHelper { }); } - static qValidateForm(dispatch, qName){ + static qValidateForm(dispatch, qName, customValidations){ dispatch({ type: qcommonActionTypes.VALIDATE_FORM, - qName + qName, + customValidations }); } diff --git a/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js b/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js index 35b2f936ce..916f724b40 100644 --- a/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js +++ b/openecomp-ui/src/sdc-app/common/reducers/JSONSchemaReducer.js @@ -99,8 +99,8 @@ function updateSchemaDataAndValidateReducer (state = {}, action, questionnaireNa genericFieldInfoClone = {...state.qgenericFieldInfo}; let formReady = true; forOwn(state.qgenericFieldInfo,(value, key) => { - let val = state.data[key] ? state.data[key] : ''; - let result = Validator.validate(key, val, state.qgenericFieldInfo[key].validations, state, {}); + let val = state.dataMap[key] ? state.dataMap[key] : ''; + let result = Validator.validate(key, val, state.qgenericFieldInfo[key].validations, state, action.customValidations); genericFieldInfoClone[key] = {...genericFieldInfoClone[key], isValid: result.isValid, errorText: result.errorText}; if (!result.isValid) { formReady = false; diff --git a/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js index 0bbb5e63be..2276984f7a 100644 --- a/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js +++ b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js @@ -16,6 +16,7 @@ import {actionTypes} from './PlainDataReducerConstants.js'; import Validator from 'nfvo-utils/Validator.js'; import forOwn from 'lodash/forOwn.js'; +import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; function updateDataAndValidateReducer(state = {}, action) { let genericFieldInfoCopy; @@ -46,6 +47,12 @@ function updateDataAndValidateReducer(state = {}, action) { forOwn(state.genericFieldInfo,(value, key) => { let val = state.data && state.data[key] ? state.data[key] : ''; let result = Validator.validate(key, val, state.genericFieldInfo[key].validations, state, {}); + if(val.choice !== undefined) { + result = Validator.validate(key, val.choice, state.genericFieldInfo[key].validations, state, {}); + } + if(val.choice !== undefined && val.choice === optionInputOther.OTHER) { + result = Validator.validate(key, val.other, state.genericFieldInfo[key].validations, state, {}); + } genericFieldInfoCopy[key] = {...genericFieldInfoCopy[key], isValid: result.isValid, errorText: result.errorText}; if (!result.isValid) { formReady = false; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js index 2b59361eef..24ba05e303 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -31,8 +31,7 @@ import OnboardActionHelper from './onboard/OnboardActionHelper.js'; import SoftwareProductComponentsMonitoringAction from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js'; import {actionTypes, enums} from './OnboardingConstants.js'; import SoftwareProductComponentsImageActionHelper from './softwareProduct/components/images/SoftwareProductComponentsImageActionHelper.js'; -import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes, - onboardingMethod as onboardingMethodTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; import ActivityLogActionHelper from 'sdc-app/common/activity-log/ActivityLogActionHelper.js'; import licenseModelOverviewActionHelper from 'sdc-app/onboarding/licenseModel/overview/licenseModelOverviewActionHelper.js'; import store from 'sdc-app/AppStore.js'; @@ -164,18 +163,10 @@ export default { const newVersion = response[0].version ? response[0].version : version; SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}); - let isFetchImageDetails = (response[0].onboardingMethod === onboardingMethodTypes.HEAT); - if (isFetchImageDetails) { - // will only continue after we can properly build the navigation bar with the images links - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion, isFetchImageDetails}).then(() => { - SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); - }); - } else { - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion, isFetchImageDetails}); - SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); - setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); - } + + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version: newVersion}); + SoftwareProductActionHelper.loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version: newVersion}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version: newVersion}); }); }, @@ -284,6 +275,13 @@ export default { version }); setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES, {softwareProductId, version, componentId}); + }, + + updateCurrentScreenVersion(dispatch, version) { + dispatch({ + type: actionTypes.SET_CURRENT_SCREEN_VERSION, + version + }); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js index 0fff513cc5..1ce8605b63 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js @@ -19,7 +19,8 @@ export const DATE_FORMAT = 'MM/DD/YYYY'; export const actionTypes = keyMirror({ SET_CURRENT_SCREEN: null, - SET_CURRENT_LICENSE_MODEL: null + SET_CURRENT_LICENSE_MODEL: null, + SET_CURRENT_SCREEN_VERSION: null, }); export const enums = keyMirror({ diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx index 1f0bef7a9f..aea5fc6406 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -415,9 +415,9 @@ export default class OnboardingPunchOut { handleStoreChange() { let {currentScreen, licenseModelList, softwareProductList, softwareProduct: {softwareProductEditor: {data = {onboardingMethod: ''}}, - softwareProductComponents: {componentsList, images: {imagesNavigationList}}, softwareProductAttachments: {heatSetup}}} = store.getState(); + softwareProductComponents: {componentsList}, softwareProductAttachments: {heatSetup}}} = store.getState(); let {onboardingMethod} = data; - let breadcrumbsData = {onboardingMethod, currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup, imagesNavigationList}; + let breadcrumbsData = {onboardingMethod, currentScreen, licenseModelList, softwareProductList, componentsList, heatSetup}; if (currentScreen.forceBreadCrumbsUpdate || !isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { this.prevBreadcrumbsData = breadcrumbsData; this.breadcrumbsPrefixSelected = false; @@ -434,7 +434,7 @@ export default class OnboardingPunchOut { } } - buildBreadcrumbs({currentScreen: {screen, props}, onboardingMethod, licenseModelList, softwareProductList, componentsList, heatSetup, imagesNavigationList}) { + buildBreadcrumbs({currentScreen: {screen, props}, onboardingMethod, licenseModelList, softwareProductList, componentsList, heatSetup}) { let screenToBreadcrumb; switch (screen) { case enums.SCREEN.ONBOARDING_CATALOG: @@ -646,15 +646,7 @@ export default class OnboardingPunchOut { }, { key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING, displayText: i18n('Monitoring') - }].filter(item => { - switch (item.key) { - case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_IMAGES: - return (onboardingMethod === onboardingMethodTypes.MANUAL || - (imagesNavigationList && imagesNavigationList[props.componentId] === true)); - default: - return true; - } - }) + }] }] ]; } diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js index 46fc58db95..e63bcb16af 100644 --- a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js @@ -16,9 +16,19 @@ import {actionTypes, enums} from './OnboardingConstants.js'; export const currentScreenReducer = (state = {forceBreadCrumbsUpdate: false, screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => { - if (action.type === actionTypes.SET_CURRENT_SCREEN) { - return action.currentScreen; - } - return state; + switch (action.type) { + case actionTypes.SET_CURRENT_SCREEN: + return action.currentScreen; + case actionTypes.SET_CURRENT_SCREEN_VERSION: + return { + ...state, + props: { + ...state.props, + version: action.version + } + }; + default: + return state; + } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js index 186f1cbc7b..788528fd75 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js @@ -23,6 +23,7 @@ import LicenseAgreementActionHelper from './licenseAgreement/LicenseAgreementAct import FeatureGroupsActionHelper from './featureGroups/FeatureGroupsActionHelper.js'; import EntitlementPoolsActionHelper from './entitlementPools/EntitlementPoolsActionHelper.js'; import LicenseKeyGroupsActionHelper from './licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; function baseUrl() { const restPrefix = Configuration.get('restPrefix'); @@ -140,6 +141,8 @@ const LicenseModelActionHelper = { newVersionId = adjustMajorVersion(version.label, 1); } + OnboardingActionHelper.updateCurrentScreenVersion(dispatch, {label: newVersionId, id: newVersionId}); + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version:{id: newVersionId, label: newVersionId}}); return Promise.resolve({id: newVersionId, label: newVersionId}); }); 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..ce4327d810 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 {default as getValue, getStrValue} from 'nfvo-utils/getValue.js'; function baseUrl(licenseModelId, version) { const restPrefix = Configuration.get('restPrefix'); @@ -24,7 +26,7 @@ function baseUrl(licenseModelId, version) { return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/versions/${versionId}/entitlement-pools`; } -function fetchEntitlementPoolsList(licenseModelId, version) { +function fetchEntitlementPoolsList(licenseModelId, version) { return RestAPIUtil.fetch(`${baseUrl(licenseModelId, version)}`); } @@ -33,13 +35,10 @@ function postEntitlementPool(licenseModelId, entitlementPool, version) { name: entitlementPool.name, description: entitlementPool.description, thresholdValue: entitlementPool.thresholdValue, - thresholdUnits: entitlementPool.thresholdUnits, - entitlementMetric: entitlementPool.entitlementMetric, + thresholdUnits: getValue(entitlementPool.thresholdUnits), 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 +46,15 @@ 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, - entitlementMetric: entitlementPool.entitlementMetric, + thresholdUnits: getValue(entitlementPool.thresholdUnits), 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 +64,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: getStrValue(limit.metric), + value: limit.value, + unit: getStrValue(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: getStrValue(limit.metric), + value: limit.value, + unit: getStrValue(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 +108,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 +180,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..497c29d14c 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} @@ -97,24 +97,33 @@ class FeatureGroupListEditorView extends React.Component { <div className='text name'>{name}</div> </div> - <div className='list-editor-item-view-field'> + <div className='list-editor-item-view-field smaller-field'> <div className='feature-groups-count-field'> - <div className='title'>{i18n('Entitlement')}</div> - <div className='title'>{i18n('Pools')}</div> + <div className='title'>{i18n('EP')}</div> <div className='feature-groups-count-ep'>{entitlementPoolsIds.length || 0}</div> </div> + </div> + <div className='list-editor-item-view-field smaller-field'> <div className='feature-groups-count-field'> - <div className='title'>{i18n('License key')}</div> - <div className='title'>{i18n('Groups')}</div> + <div className='title'>{i18n('LKG')}</div> <div className='feature-groups-count-lk'>{licenseKeyGroupsIds.length || 0}</div> </div> </div> <div className='list-editor-item-view-field'> + <div className='feature-groups-count-field'> + <div className='title title-no-wrap'>{i18n('Manufacturer Reference Number')}</div> + <div className='feature-groups-mrn-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..f22080a75c 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 {default as getValue, getStrValue} 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: getStrValue(limit.metric), + value: limit.value, + unit: getStrValue(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: getStrValue(limit.metric), + value: limit.value, + unit: getStrValue(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..5c4e50d673 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx @@ -0,0 +1,226 @@ +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'; +import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; + +const LimitPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + metric: React.PropTypes.shape({ + choice: React.PropTypes.string, + other: React.PropTypes.string + }), + value: React.PropTypes.string, + aggregationFunction: React.PropTypes.string, + time: React.PropTypes.string, + unit: React.PropTypes.shape({ + choice: React.PropTypes.string, + other: React.PropTypes.string + }) +}); + +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}> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={true} + onEnumChange={metric => onDataChanged({metric:{choice: metric, other: ''}}, + LIMITS_FORM_NAME, {metric: this.validateChoiceWithOther})} + onOtherChange={metric => onDataChanged({metric:{choice: optionInputOther.OTHER, + other: metric}}, LIMITS_FORM_NAME, {metric: this.validateChoiceWithOther})} + label={i18n('Metric')} + data-test-id='limit-editor-metric' + type='select' + required={true} + selectedEnum={metric && metric.choice} + otherValue={metric && metric.other} + values={selectValues.METRIC} + isValid={genericFieldInfo.metric.isValid} + errorText={genericFieldInfo.metric.errorText} /> + </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='text'/> + </GridItem> + <GridItem> + <InputOptions + onInputChange={()=>{}} + isMultiSelect={false} + isRequired={false} + onEnumChange={unit => onDataChanged({unit:{choice: unit, other: ''}}, + LIMITS_FORM_NAME)} + onOtherChange={unit => onDataChanged({unit:{choice: optionInputOther.OTHER, + other: unit}}, LIMITS_FORM_NAME)} + label={i18n('Units')} + data-test-id='limit-editor-units' + type='select' + required={false} + selectedEnum={unit && unit.choice} + otherValue={unit && unit.other} + values={selectValues.UNIT} + isValid={genericFieldInfo.unit.isValid} + errorText={genericFieldInfo.unit.errorText} /> + </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')}; + } + + validateChoiceWithOther(value) { + let chosen = value.choice; + // if we have an empty multiple select we have a problem since it's required + if (value.choices) { + if (value.choices.length === 0) { + return Validator.validate('field', '', [{type: 'required', data: true}]); + } else { + // continuing validation with the first chosen value in case we have the 'Other' field + chosen = value.choices[0]; + } + } + if (chosen !== optionInputOther.OTHER) { + return Validator.validate('field', chosen, [{type: 'required', data: true}]); + } else { // when 'Other' was chosen, validate other value + return Validator.validate('field', value.other, [{type: 'required', data: true}]); + } + } + + submit() { + if (!this.props.formReady) { + this.props.onValidateForm(LIMITS_FORM_NAME); + } else { + this.props.onSubmit(); + } + } +} + +export default LimitEditor; 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..8ac845a1a7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorActionHelper.js @@ -0,0 +1,54 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {actionTypes, selectValues} from './LimitEditorConstants.js'; +import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx'; + + +const LimitEditorActionHelper = { + openLimitsEditor(dispatch, {limit}) { + let modifiedLimit = {...limit}; + if (limit && limit.metric) { + modifiedLimit.metric = LimitEditorActionHelper.createChoiceObject(modifiedLimit.metric, selectValues.METRIC); + } + if (limit && limit.unit) { + modifiedLimit.unit = LimitEditorActionHelper.createChoiceObject(modifiedLimit.unit, selectValues.UNIT); + } + dispatch({type: actionTypes.OPEN, limitItem: modifiedLimit}); + }, + + closeLimitsEditor(dispatch) { + dispatch({type: actionTypes.CLOSE}); + }, + + createChoiceObject(value, optionsList) { + let option = optionsList.find(function(item) { return item.enum === value; }); + if (!option) { + return { + choice: optionInputOther.OTHER, + other: value + }; + } + else { + return { + choice: value, + other: '' + }; + } + } +}; + +export default LimitEditorActionHelper; 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..9829e696c3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js @@ -0,0 +1,64 @@ +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: 'BWTH', title: 'BWTH'}, + {enum: 'Country', title: 'Country'}, + {enum: 'Session', title: 'Session'}, + {enum: 'LoB', title: 'LoB'}, + {enum: 'Site', title: 'Site'}, + {enum: 'Usage', title: 'Usage'} + ], + UNIT: [ + {enum: '', title: i18n('please select…')}, + {enum: 'trunk', title: 'Trunks'}, + {enum: 'user', title: 'Users'}, + {enum: 'subscriber', title: 'Subscribers'}, + {enum: 'session', title: 'Sessions'}, + {enum: 'tenant', title: 'Tenants'}, + {enum: 'token', title: 'Tokens'}, + {enum: 'seats', title: 'Seats'}, + {enum: 'TB', title: 'TB'}, + {enum: 'GB', title: 'GB'}, + {enum: 'MB', title: 'MB'} + ], + AGGREGATION_FUNCTION: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Peak', title: 'Peak'}, + {enum: 'Average', title: 'Average'} + ], + TIME: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Day', title: 'Day'}, + {enum: 'Month', title: 'Month'}, + {enum: 'Hour', title: 'Hour'}, + {enum: 'Minute', title: 'Minute'}, + {enum: 'Second', title: 'Second'}, + {enum: 'Milli-Second', title: 'Milli-Second'} + ] + +}; + +export const limitType = { + SERVICE_PROVIDER: 'ServiceProvider', + VENDOR: 'Vendor' +}; + +export const defaultState = { + LIMITS_EDITOR_DATA: { + metric: {choice: '', other: ''}, + } +}; + +export const NEW_LIMIT_TEMP_ID = 'NEW_LIMIT_TEMP_ID'; 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..de9e7c8c38 --- /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: [] + }, + 'value' : { + isValid: true, + errorText: '', + validations: [{type: 'required', data: true}] + }, + 'unit' : { + isValid: true, + errorText: '', + validations: [] + }, + 'aggregationFunction' : { + isValid: true, + errorText: '', + validations: [] + }, + 'time' : { + isValid: true, + errorText: '', + validations: [] + } + } + }; + case actionTypes.CLOSE: + return {}; + default: + return state; + } +}; 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..b144f63968 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/Limits.jsx @@ -0,0 +1,111 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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, value, metric, aggregationFunction = '', time = ''} = limit; + const timeLabel = time ? `per ${time}` : ''; + let metricOption = selectValues.METRIC.find(item => item.enum === metric); + metricOption = metricOption ? metricOption.title : metric; + + 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'>{`${metricOption} ${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.deleteLimit(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()); + } + + deleteLimit(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/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitsServer.js index 20d1f5dd18..1b8ecb9d94 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageNavigationReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitsServer.js @@ -13,20 +13,34 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ -import {actionTypes} from './SoftwareProductComponentsImageConstants.js'; -export default (state = {}, action) => { - switch (action.type) { +// items/{itemId}/users - case actionTypes.IMAGES_LIST_UPDATE: - if (action.componentId) { - return { - ...state, - [action.componentId] : (action.response && action.response.length > 0) - }; - } - return state; - default: - return state; +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/LicenseModelDescriptionEdit.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx index 0c0103fc10..9232402b75 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/LicenseModelDescriptionEdit.jsx @@ -18,6 +18,7 @@ import Input from 'nfvo-components/input/validation/Input.jsx'; class LicenseModelDescriptionEdit extends React.Component { render() { + //TODO check if buttons let {onDataChanged, description, genericFieldInfo} = this.props; let saveButtonClassName = (genericFieldInfo.description.isValid) ? 'description-save' : 'description-save disabled'; return( 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/SummaryCountItem.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx index 66f2cc6838..6ec84e16ca 100644 --- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/overview/summary/SummaryCountItem.jsx @@ -14,15 +14,17 @@ * permissions and limitations under the License. */ import React from 'react'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; function SummaryCountItem ({name, counter, onAdd, onNavigate, isReadOnlyMode}) { + //TODO check for buttons return( <div className='summary-count-item'> <div className='summary-name-and-count' onClick={onNavigate}> <span className='item-name' onClick={onNavigate}>{name}</span> <span className='item-count' onClick={onNavigate} data-test-id={'vlm-summary-vendor-counter-' + name.toLowerCase().replace(/\s/g,'-')}>({counter})</span> </div> - <div className={isReadOnlyMode ? 'add-button disabled' : 'add-button'} onClick={onAdd} data-test-id={'vlm-summary-vendor-add-btn-' + name.toLowerCase().replace(/\s/g,'-')}/> + <SVGIcon name='plusCircle' disabled={isReadOnlyMode} color='secondary' onClick={onAdd} data-test-id={'vlm-summary-vendor-add-btn-' + name.toLowerCase().replace(/\s/g,'-')}/> </div> ); } 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..deb4550347 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,12 @@ * 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 SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; import licenseModelOverviewActionHelper from '../licenseModelOverviewActionHelper.js'; import LicenseModelActionHelper from '../../LicenseModelActionHelper.js'; @@ -46,9 +49,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 +63,17 @@ 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} + <SVGIcon name='pencil'/> + </div> + )} </div> ); } @@ -80,7 +85,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); - diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx index 3b3e2fcf40..5939499ec5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogItemDetails.jsx @@ -49,7 +49,7 @@ const CatalogTileVendorName = ({vendorName, catalogItemTypeClass}) => { }; const CatalogTileItemName = ({name}) => ( - <OverlayTrigger placement='top' overlay={tooltip(name && name.toUpperCase())}> + <OverlayTrigger placement='top' overlay={tooltip(name)}> <div className='catalog-tile-item-name'>{name}</div> </OverlayTrigger> ); diff --git a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx index 51702e6d36..7664f6abaa 100644 --- a/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/onboard/CatalogList.jsx @@ -40,6 +40,7 @@ const CatalogList = ({children, onAddVLM, onAddVSP, vendorPageOptions}) => ( ); const CreateItemTile = ({onClick, dataTestId, title, className = ''}) => { + //TODO check for buttons return ( <div className={'create-catalog-item tile ' + className} onClick={() => onClick()} data-test-id={dataTestId}> <div className='create-item-plus-icon'><SVGIcon name='plus' /></div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js index 07d6c740e0..c9c95f359e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -36,7 +36,7 @@ function getActiveNavigationId(screen, componentId) { return activeItemId; } -const buildComponentNavigationBarGroups = ({componentId, meta, hasImages}) => { +const buildComponentNavigationBarGroups = ({componentId, meta}) => { const groups = ([ { id: navigationItems.GENERAL + '|' + componentId, @@ -67,7 +67,6 @@ const buildComponentNavigationBarGroups = ({componentId, meta, hasImages}) => { id: navigationItems.IMAGES + '|' + componentId, name: i18n('Images'), disabled: false, - hidden: (!hasImages), meta }, { id: navigationItems.PROCESS_DETAILS + '|' + componentId, @@ -85,7 +84,7 @@ const buildComponentNavigationBarGroups = ({componentId, meta, hasImages}) => { return groups; }; -const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds, imagesNavigationList}) => { +const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) => { const {softwareProductEditor: {data: currentSoftwareProduct = {}}} = softwareProduct; const {id, name, onboardingMethod} = currentSoftwareProduct; const groups = [{ @@ -148,8 +147,7 @@ const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, co name: displayName, meta, expanded: mapOfExpandedIds[navigationItems.COMPONENTS + '|' + id] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, - items: buildComponentNavigationBarGroups({componentId: id, meta, - hasImages : (onboardingMethod === onboardingMethodTypes.MANUAL || imagesNavigationList[id] === true)}) + items: buildComponentNavigationBarGroups({componentId: id, meta}) })) ] } @@ -193,12 +191,12 @@ function buildMeta({softwareProduct, componentId, softwareProductDependencies}) const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { const {softwareProductEditor, softwareProductComponents, softwareProductDependencies} = softwareProduct; const {mapOfExpandedIds = []} = softwareProductEditor; - const {componentsList = [], images: {imagesNavigationList}} = softwareProductComponents; + const {componentsList = []} = softwareProductComponents; const meta = buildMeta({softwareProduct, componentId, softwareProductDependencies}); return { versionControllerProps: buildVersionControllerProps(softwareProduct), - navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds, imagesNavigationList}), + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}), meta }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js index d6ba86ad6e..d4bee0a876 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -251,8 +251,7 @@ const SoftwareProductActionHelper = { processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){ return validateHeatCandidate(softwareProductId, version).then(response => { if (response.status === 'Success') { - let isFetchImageDetails = true; - SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails}); + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}); } }); @@ -410,6 +409,7 @@ const SoftwareProductActionHelper = { } }); const newVersionId = adjustMajorVersion(version.label, 1); + OnboardingActionHelper.updateCurrentScreenVersion(dispatch, {label: newVersionId, id: newVersionId}); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}}); return Promise.resolve({newVersion: {id: newVersionId}}); }); @@ -439,6 +439,7 @@ const SoftwareProductActionHelper = { newVersionId = adjustMinorVersion(version.label, -1); break; } + OnboardingActionHelper.updateCurrentScreenVersion(dispatch, {label: newVersionId, id: newVersionId}); SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}}); return Promise.resolve({newVersion: {id: newVersionId}}); }); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js index 5248c4e8fd..977a76ac69 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -35,7 +35,6 @@ import SoftwareProductComponentsNICListReducer from './components/network/Softwa import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; import SoftwareProductComponentsImageListReducer from './components/images/SoftwareProductComponentsImageListReducer.js'; import SoftwareProductComponentsImageEditorReducer from './components/images/SoftwareProductComponentsImageEditorReducer.js'; -import SoftwareProductComponentsImageNavigationReducer from './components/images/SoftwareProductComponentsImageNavigationReducer.js'; import SoftwareProductComponentsNICCreationReducer from './components/network/NICCreation/NICCreationReducer.js'; import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; import SoftwareProductComponentsComputeFlavorListReducer from './components/compute/computeComponents/computeFlavor/ComputeFlavorListReducer.js'; @@ -83,7 +82,6 @@ export default combineReducers({ }), images: combineReducers({ imagesList: SoftwareProductComponentsImageListReducer, - imagesNavigationList: SoftwareProductComponentsImageNavigationReducer, imageEditor: createPlainDataReducer(createComposedJSONSchemaReducer(IMAGE_QUESTIONNAIRE, SoftwareProductComponentsImageEditorReducer)) }), computeFlavor: combineReducers({ diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx index ef4aecf568..3da26cc20f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx @@ -18,7 +18,7 @@ import Tabs from 'react-bootstrap/lib/Tabs.js'; import Tab from 'react-bootstrap/lib/Tab.js'; import {tabsMapping} from './SoftwareProductAttachmentsConstants.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import Icon from 'nfvo-components/icon/Icon.jsx'; +import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import HeatValidation from './validation/HeatValidation.js'; class HeatScreenView extends Component { @@ -38,27 +38,32 @@ class HeatScreenView extends Component { <div className='vsp-attachments-view'> <div className='attachments-view-controllers'> {(this.state.activeTab === tabsMapping.SETUP) && - <Icon - iconClassName={heatDataExist ? '' : 'disabled'} - className={heatDataExist ? '' : 'disabled'} - image='download' + <SVGIcon + disabled={heatDataExist ? false : true} + name='download' + className='icon-component' label={i18n('Download HEAT')} + labelPosition='right' + color='secondary' onClick={heatDataExist ? () => onDownload({heatCandidate: heatSetup, isReadOnlyMode, version}) : undefined} data-test-id='download-heat'/>} {(this.state.activeTab === tabsMapping.VALIDATION && softwareProductId) && - <Icon - iconClassName={this.props.goToOverview ? '' : 'disabled'} - className={`go-to-overview-icon ${this.props.goToOverview ? '' : 'disabled'}`} - labelClassName='go-to-overview-label' + <SVGIcon + disabled={this.props.goToOverview !== true} onClick={this.props.goToOverview ? () => onGoToOverview({version}) : undefined} - image='go-to-overview' + name='proceedToOverview' + className='icon-component' label={i18n('Go to Overview')} + labelPosition='right' + color={this.props.goToOverview ? 'primary' : 'secondary'} data-test-id='go-to-overview'/>} - <Icon - image='upload' + <SVGIcon + name='upload' + className='icon-component' label={i18n('Upload New HEAT')} - className={isReadOnlyMode ? 'disabled' : ''} - iconClassName={isReadOnlyMode ? 'disabled' : ''} + labelPosition='right' + color='secondary' + disabled={isReadOnlyMode} onClick={evt => {this.refs.hiddenImportFileInput.click(evt);}} data-test-id='upload-heat'/> <input @@ -87,9 +92,9 @@ class HeatScreenView extends Component { } handleTabPress(key) { - let {heatSetup, heatSetupCache, onProcessAndValidate, isReadOnlyMode, version} = this.props; + let {heatSetup, heatSetupCache, onProcessAndValidate, isReadOnlyMode, version} = this.props; switch (key) { - case tabsMapping.VALIDATION: + case tabsMapping.VALIDATION: onProcessAndValidate({heatData: heatSetup, heatDataCache: heatSetupCache, isReadOnlyMode, version}).then( () => this.setState({activeTab: tabsMapping.VALIDATION}) ); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js index 53143647a3..87953bb8aa 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupActionHelper.js @@ -17,8 +17,8 @@ import {actionTypes} from './HeatSetupConstants.js'; import isEqual from 'lodash/isEqual.js'; import cloneDeep from 'lodash/cloneDeep.js'; import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; +// import i18n from 'nfvo-utils/i18n/i18n.js'; +// import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; export default { @@ -56,7 +56,11 @@ export default { dispatch({type: actionTypes.ADD_ALL_UNASSIGNED_TO_ARTIFACTS}); }, - heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) { + heatSetupLeaveConfirmation() { + return Promise.resolve(); + } + + /*heatSetupLeaveConfirmation(dispatch, {softwareProductId, heatSetup, heatSetupCache}) { return new Promise((resolve, reject) => { if (isEqual({...heatSetup, softwareProductId}, heatSetupCache)) { resolve(); @@ -73,5 +77,5 @@ export default { }); } }); - } + }*/ }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx index 901a583e24..17b3179d01 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupView.jsx @@ -20,7 +20,6 @@ import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; import FormControl from 'react-bootstrap/lib/FormControl.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import SelectInput from 'nfvo-components/input/SelectInput.jsx'; -import Icon from 'nfvo-components/icon/Icon.jsx'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import {fileTypes} from './HeatSetupConstants.js'; import {tabsMapping} from '../SoftwareProductAttachmentsConstants.js'; @@ -52,7 +51,7 @@ class SortableModuleFileList extends Component { render() { - let {unassigned, onModuleRename, onModuleDelete, onModuleAdd, onBaseAdd, onModuleFileTypeChange, isBaseExist} = this.props; + let {unassigned, onModuleRename, onModuleDelete, onModuleAdd, onBaseAdd, onModuleFileTypeChange, isBaseExist, isReadOnlyMode} = this.props; const childProps = module => ({ module, onModuleRename, @@ -68,19 +67,17 @@ class SortableModuleFileList extends Component { items={this.state.data} draggingIndex={this.state.draggingIndex} sortId={i} - outline='list'><ModuleFile {...childProps(item)} /></SortableListItem> + outline='list'><ModuleFile {...childProps(item)} isReadOnlyMode={this.props.isReadOnlyMode} /></SortableListItem> ); }, this); return ( - <div className='modules-list-wrapper'> + <div className={`modules-list-wrapper ${(listItems.length > 0) ? 'modules-list-wrapper-divider' : ''}`}> <div className='modules-list-header'> - <div className='modules-list-controllers'> - {!isBaseExist && <Button btnType='link' onClick={onBaseAdd} disabled={unassigned.length === 0}>{i18n('Add Base')}</Button>} - <Button btnType='link' onClick={onModuleAdd} disabled={unassigned.length === 0}>{i18n('Add Module')}</Button> - </div> + {!isBaseExist && <div><Button btnType='link' onClick={onBaseAdd} disabled={isReadOnlyMode || unassigned.length === 0}>{i18n('Add Base')}</Button></div>} + <div><Button btnType='link' onClick={onModuleAdd} disabled={isReadOnlyMode || unassigned.length === 0}>{i18n('Add Module')}</Button></div> </div> - <ul>{listItems}</ul> + {(listItems.length > 0) && <ul>{listItems}</ul>} </div> ); } @@ -89,20 +86,24 @@ class SortableModuleFileList extends Component { const tooltip = (name) => <Tooltip id='tooltip-bottom'>{name}</Tooltip>; const UnassignedFileList = (props) => { return ( - <div className='unassigned-files'> + <div> + <div className='modules-list-header'/> + <div className='unassigned-files'> <div className='unassigned-files-title'>{i18n('UNASSIGNED FILES')}</div> <div className='unassigned-files-list'>{props.children}</div> + </div> </div> ); }; const EmptyListContent = props => { - let {onClick, heatDataExist} = props; + let {onClick, heatDataExist, isReadOnlyMode} = props; let displayText = heatDataExist ? 'All Files Are Assigned' : ''; return ( <div className='go-to-validation-button-wrapper'> <div className='all-files-assigned'>{i18n(displayText)}</div> - {heatDataExist && <div className={'link'} onClick={onClick} data-test-id='go-to-validation'>{i18n('Proceed To Validation')}<SVGIcon name='angleRight'/></div>} + {heatDataExist && <SVGIcon disabled={isReadOnlyMode} name='angleRight' onClick={onClick} + data-test-id='go-to-validation' label={i18n('Proceed To Validation')} labelPosition='left' color='primary'/>} </div> ); }; @@ -112,14 +113,11 @@ const UnassignedFile = (props) => ( </OverlayTrigger> ); -const AddOrDeleteVolumeFiels = ({add = true, onAdd, onDelete}) => { +const AddOrDeleteVolumeFiles = ({add = true, onAdd, onDelete, isReadOnlyMode}) => { const displayText = add ? 'Add Volume Files' : 'Delete Volume Files'; const action = add ? onAdd : onDelete; return ( - <div className='add-or-delete-volumes' onClick={action}> - <SVGIcon name={add ? 'plus' : 'close'} /> - <span>{i18n(displayText)}</span> - </div> + <Button disabled={isReadOnlyMode} onClick={action} btnType='link' className='add-or-delete-volumes' iconName={add ? 'plus' : 'close'}>{i18n(displayText)}</Button> ); }; @@ -195,14 +193,14 @@ class ModuleFile extends Component { } render() { - const {module: {name, isBase, yaml, env, vol, volEnv}, onModuleDelete, files, onModuleFileTypeChange} = this.props; + const {module: {name, isBase, yaml, env, vol, volEnv}, onModuleDelete, files, onModuleFileTypeChange, isReadOnlyMode} = this.props; const {displayVolumes} = this.state; const moduleType = isBase ? 'BASE' : 'MODULE'; return ( <div className='modules-list-item' data-test-id='module-item'> <div className='modules-list-item-controllers'> <div className='modules-list-item-filename'> - <Icon image={isBase ? 'base' : 'module'} iconClassName='heat-setup-module-icon' /> + <SVGIcon name={isBase ? 'base' : 'module'} color='primary' iconClassName='heat-setup-module-icon' /> <span className='module-title-by-type'>{`${moduleType}: `}</span> <div className={`text-and-icon ${this.state.isInNameEdit ? 'in-edit' : ''}`}> {this.renderNameAccordingToEditState()} @@ -235,7 +233,7 @@ class ModuleFile extends Component { files={files} selected={volEnv} onChange={onModuleFileTypeChange}/>} - <AddOrDeleteVolumeFiels onAdd={() => this.setState({displayVolumes: true})} onDelete={() => this.deleteVolumeFiles()} add={!displayVolumes}/> + <AddOrDeleteVolumeFiles isReadOnlyMode={isReadOnlyMode} onAdd={() => this.setState({displayVolumes: true})} onDelete={() => this.deleteVolumeFiles()} add={!displayVolumes}/> </div> </div> ); @@ -245,15 +243,15 @@ class ModuleFile extends Component { class ArtifactOrNestedFileList extends Component { render() { - let {type, title, selected, options, onSelectChanged, onAddAllUnassigned} = this.props; + let {type, title, selected, options, onSelectChanged, onAddAllUnassigned, isReadOnlyMode, headerClassName} = this.props; return ( - <div className={`artifact-files ${type === 'nested' ? 'nested' : ''}`}> + <div className={`artifact-files ${type === 'nested' ? 'nested' : ''} ${headerClassName} `}> <div className='artifact-files-header'> <span> - {type === 'artifact' && (<Icon image='artifacts' iconClassName='heat-setup-module-icon' />)} + {type === 'artifact' && (<SVGIcon color='primary' name='artifacts' iconClassName='heat-setup-module-icon' />)} {`${title}`} </span> - {type === 'artifact' && <span className='add-all-unassigned' onClick={onAddAllUnassigned}>{i18n('Add All Unassigned Files')}</span>} + {type === 'artifact' && <Button disabled={isReadOnlyMode} btnType='link' className='add-all-unassigned' onClick={onAddAllUnassigned}>{i18n('Add All Unassigned Files')}</Button>} </div> {type === 'nested' ? ( <ul className='nested-list'>{selected.map(nested => @@ -294,6 +292,7 @@ class SoftwareProductHeatSetupView extends Component { <div className='heat-setup-view-modules-and-artifacts'> <SortableModuleFileList {...this.props} + isReadOnlyMode={this.props.isReadOnlyMode} artifacts={formattedArtifacts} unassigned={formattedUnassigned}/> <ArtifactOrNestedFileList @@ -302,11 +301,14 @@ class SoftwareProductHeatSetupView extends Component { options={formattedUnassigned} selected={formattedArtifacts} onSelectChanged={onArtifactListChange} + isReadOnlyMode={this.props.isReadOnlyMode} + headerClassName={(modules && modules.length > 0) ? 'with-list-items' : ''} onAddAllUnassigned={onAddAllUnassigned}/> <ArtifactOrNestedFileList type={'nested'} title={i18n('NESTED HEAT FILES')} options={[]} + isReadOnlyMode={this.props.isReadOnlyMode} selected={nested}/> </div> <UnassignedFileList> @@ -316,6 +318,7 @@ class SoftwareProductHeatSetupView extends Component { : (<EmptyListContent heatDataExist={heatDataExist} + isReadOnlyMode={this.props.isReadOnlyMode} onClick={() => this.processAndValidateHeat({modules, unassigned, artifacts, nested}, heatSetupCache)}/>) } </UnassignedFileList> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx index f2d5de4dff..62dcb82db1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx @@ -16,19 +16,18 @@ import React, {Component, PropTypes} from 'react'; import classNames from 'classnames'; import Collapse from 'react-bootstrap/lib/Collapse.js'; -import Icon from 'nfvo-components/icon/Icon.jsx'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js'; const leftPanelWidth = 250; const typeToIcon = Object.freeze({ - heat: 'heat', - volume: 'volume', + heat: 'nestedHeat', + volume: 'base', network: 'network', - artifact: 'validation-artifacts', + artifact: 'artifacts', env: 'env', - other: 'validation-other' + other: 'other' }); @@ -74,7 +73,7 @@ function HeatFileTreeRow(props) { { <span className='tree-node-icon'> - <Icon image={typeToIcon[node.type]} iconClassName={selectedNode === node.name ? 'selected' : ''}/> + <SVGIcon name={typeToIcon[node.type]} color={selectedNode === node.name ? 'primary' : 'secondary'}/> </span> } { @@ -94,7 +93,7 @@ function HeatFileTreeHeader(props) { <div onClick={() => props.selectNode(nodeFilters.ALL)} className={classNames({'attachments-tree-header': true, 'header-selected' : props.selectedNode === nodeFilters.ALL})} data-test-id='validation-tree-header'> <div className='tree-header-title' > - <Icon image='zip' iconClassName={classNames(props.selectedNode === nodeFilters.ALL ? 'selected' : '', 'header-icon')} /> + <SVGIcon name='zip' color={props.selectedNode === nodeFilters.ALL ? 'primary' : ''} iconClassName='header-icon' /> <span className={classNames({'tree-header-title-text' : true, 'tree-header-title-selected' : props.selectedNode === nodeFilters.ALL})}>{i18n(`HEAT${hasErrors ? ' (Draft)' : ''}`)}</span> </div> @@ -200,12 +199,13 @@ class HeatMessageBoard extends Component { } renderError(error) { let rand = Math.random() * (3000 - 1) + 1; + console.log(this.props.selectedNode ); return ( <div key={error.name + error.errorMessage + error.parentName + rand} className='error-item' data-test-id='validation-error'> {error.level === errorLevels.WARNING ? - <SVGIcon name='exclamationTriangleLine' iconClassName='large' /> : <Icon image='error-lg' /> } + <SVGIcon name='exclamationTriangleLine' iconClassName='large' color='warning' /> : <SVGIcon iconClassName='large' color='negative' /> } <span className='error-item-file-type'> { (this.props.selectedNode === nodeFilters.ALL) ? @@ -217,7 +217,7 @@ class HeatMessageBoard extends Component { {i18n(error.errorMessage)} </span> </span> : - i18n(error.errorMesage) + i18n(error.errorMessage) } </span> </div> @@ -234,18 +234,14 @@ class ErrorsAndWarningsCount extends Component { if (!errors) { return null; } - let errIcon = 'error'; let {size} = this.props; - if (size && size === 'large') { - errIcon += '-lg'; - } return (<div className='counters'> {(errors.errorCount > 0) && <div className='counter'> - <Icon image={errIcon} iconClassName='counter-icon'/> - <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>{errors.errorCount}</div> + <SVGIcon name='error' color='negative' iconClassName={size}/> + <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>={errors.errorCount}</div> </div>} {(errors.warningCount > 0) && <div className='counter'> - <SVGIcon name='exclamationTriangleLine' iconClassName={size} /> + <SVGIcon name='exclamationTriangleLine' iconClassName={size} color='warning'/> <div className={'warning-text ' + (size ? size : '')} data-test-id='validation-warning-count'>{errors.warningCount}</div> </div>} </div>); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js index 71dc8325ad..8085c875f4 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -18,7 +18,6 @@ import Configuration from 'sdc-app/config/Configuration.js'; import {actionTypes, COMPONENTS_QUESTIONNAIRE} from './SoftwareProductComponentsConstants.js'; import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js'; -import SoftwareProductComponentsImageActionHelper from './images/SoftwareProductComponentsImageActionHelper.js'; import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; function baseUrl(softwareProductId, version) { @@ -69,32 +68,12 @@ function postSoftwareProductComponent(softwareProductId, vspComponent, version) const SoftwareProductComponentsActionHelper = { - fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails = false}) { + fetchSoftwareProductComponents(dispatch, {softwareProductId, version}) { return fetchSoftwareProductComponents(softwareProductId, version).then(response => { - let componentImagesCalls = []; - if (isFetchImageDetails && response.listCount) { - response.results.map(component => { - let componentId = component.id; - componentImagesCalls[componentImagesCalls.length] = - SoftwareProductComponentsImageActionHelper.fetchImagesList(dispatch, { - softwareProductId, - componentId, - version - }); - - }); - return Promise.all(componentImagesCalls).then(() => { - dispatch({ - type: actionTypes.COMPONENTS_LIST_UPDATE, - componentsList: response.results - }); - }); - } else { - dispatch({ - type: actionTypes.COMPONENTS_LIST_UPDATE, - componentsList: response.results - }); - } + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); }); }, diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/ImageValidations.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/ImageValidations.js new file mode 100644 index 0000000000..2483d0aaa2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/ImageValidations.js @@ -0,0 +1,6 @@ + +import Validator from 'nfvo-utils/Validator.js'; + +export const imageCustomValidations = { + 'version': value => Validator.validate('version', value, [{type: 'required', data: true}]) +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js index 49d891c9df..5c81f05e80 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js @@ -54,9 +54,12 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId, version}) onDataChanged: (deltaData) => ValidationHelper.dataChanged(dispatch, {deltaData, formName: forms.IMAGE_EDIT_FORM}), onSubmit: ({data, qdata}) => SoftwareProductComponentsImageActionHelper.saveImageDataAndQuestionnaire(dispatch, {softwareProductId, componentId, version, data, qdata}), onCancel: () => SoftwareProductComponentsImageActionHelper.closeImageEditor(dispatch), - onValidateForm: () => ValidationHelper.validateForm(dispatch, forms.IMAGE_EDIT_FORM), - onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, - qName: IMAGE_QUESTIONNAIRE}), + onValidateForm: customValidations => { + ValidationHelper.validateForm(dispatch, forms.IMAGE_EDIT_FORM); + ValidationHelper.qValidateForm(dispatch, IMAGE_QUESTIONNAIRE, customValidations); + }, + onQDataChanged: (deltaData, customValidations) => ValidationHelper.qDataChanged(dispatch, {deltaData, + qName: IMAGE_QUESTIONNAIRE, customValidations}), }; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js index 0ab785a97f..7c357429e5 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorReducer.js @@ -30,7 +30,7 @@ export default (state = {}, action) => { 'fileName' : { isValid: true, errorText: '', - validations: [{type: 'required', data: true}, {type: 'validateName', data: true}] + validations: [{type: 'required', data: true}] } }, formName: forms.IMAGE_EDIT_FORM diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx index 300f8edcc3..7c1a3f5b55 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditorView.jsx @@ -20,6 +20,7 @@ import Form from 'nfvo-components/input/validation/Form.jsx'; import FileDetails from './imagesEditorComponents/FileDetails.jsx'; import ImageDetails from './imagesEditorComponents/ImageDetails.jsx'; +import {imageCustomValidations} from './ImageValidations.js'; class SoftwareProductComponentsImageEditorView extends React.Component { static propTypes = { @@ -44,7 +45,7 @@ class SoftwareProductComponentsImageEditorView extends React.Component { isValid={isFormValid} formReady={formReady} submitButtonText={editingMode ? i18n('Save') : i18n('Create')} - onValidateForm={() => onValidateForm() } + onValidateForm={() => onValidateForm(imageCustomValidations) } className='vsp-components-image-editor'> <div className='editor-data'> <FileDetails @@ -56,7 +57,7 @@ class SoftwareProductComponentsImageEditorView extends React.Component { isManual={isManual} dataMap={dataMap} onQDataChanged={onQDataChanged}/> - {editingMode && <ImageDetails dataMap={dataMap}qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} + {editingMode && <ImageDetails dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} </div> </Form>} </div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js index 86c4e072d4..fb3bd35eb2 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageList.js @@ -77,9 +77,10 @@ const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { image, isReadOnlyMode, softwareProductId, componentId, version, modalClassName: 'image-modal-edit'} ); }, - onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + onSubmit: (version, qdata) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, + version, qdata}); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx index ccf5b9d6b1..54def08fc1 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageListView.jsx @@ -125,8 +125,8 @@ class SoftwareProductComponentsImageListView extends React.Component { } save() { - let {onSubmit, qdata} = this.props; - return onSubmit({qdata}); + let {onSubmit, qdata, version} = this.props; + return onSubmit(version, qdata); } } export default SoftwareProductComponentsImageListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx index ca58b697a2..2e9ab417d8 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/FileDetails.jsx @@ -40,6 +40,7 @@ const FileDetails = ({editingMode, fileName, onDataChanged, isManual, dataMap, o type='text' className='image-filename'/> </GridItem> + {!editingMode && <div className='note-text'>{i18n('After image creation you must go to Edit Image and add File Version')}</div>} {editingMode && <Version isManual={isManual} dataMap={dataMap} qgenericFieldInfo={qgenericFieldInfo} onQDataChanged={onQDataChanged}/>} {editingMode && <Format isManual={isManual} qgenericFieldInfo={qgenericFieldInfo} dataMap={dataMap} onQDataChanged={onQDataChanged}/>} </GridSection> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx index 3cac9a51b8..7dd577b8c9 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/images/imagesEditorComponents/Version.jsx @@ -17,6 +17,7 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Input from 'nfvo-components/input/validation/Input.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; +import {imageCustomValidations} from '../ImageValidations.js'; const Version = ({isManual, dataMap, qgenericFieldInfo, onQDataChanged}) => { @@ -28,7 +29,8 @@ const Version = ({isManual, dataMap, qgenericFieldInfo, onQDataChanged}) => { type='text' className='image-version' label={i18n('Version')} - onChange={(version) => onQDataChanged({'version' : version})} + isRequired={true} + onChange={(version) => onQDataChanged({'version' : version}, {'version' : imageCustomValidations['version']})} isValid={qgenericFieldInfo['version'].isValid} errorText={qgenericFieldInfo['version'].errorText} value={dataMap['version']}/> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx index 9ae9e359b0..8a82f54901 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -50,6 +50,7 @@ const pointers = [ added: 'Please describe.' } ]; +//TODO check for buttons const TextAreaItem = ({item, toggle, expanded, genericFieldInfo, dataMap, onQDataChanged}) => ( <GridItem colSpan={3} key={item.key} > diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx index 2ad48ec84b..2d5a965c40 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -15,10 +15,8 @@ */ import React, {Component, PropTypes} from 'react'; import Dropzone from 'react-dropzone'; -import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; -import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar.js'; -import Button from 'react-bootstrap/lib/Button.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; +import Button from 'sdc-ui/lib/react/Button.js'; +import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx'; import {fileTypes, type2Title, type2Name} from './SoftwareProductComponentsMonitoringConstants.js'; @@ -59,7 +57,7 @@ class SoftwareProductComponentsMonitoringView extends Component { let typeDisplayName = type2Title[type]; return ( <Dropzone - className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`} + className={`dropzone ${this.state.dragging ? 'active-dragging' : ''}`} onDrop={(acceptedFiles, rejectedFiles) => this.handleImport(acceptedFiles, rejectedFiles, {isReadOnlyMode, type, refAndName})} onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} onDragLeave={() => this.setState({dragging:false})} @@ -71,7 +69,7 @@ class SoftwareProductComponentsMonitoringView extends Component { disabled> <div className='draggable-wrapper'> <div className='section-title'>{typeDisplayName}</div> - {fileName ? this.renderUploadedFileName(fileName, type) : this.renderUploadButton(refAndName)} + {fileName ? this.renderUploadedFileName(fileName, type, isReadOnlyMode) : this.renderUploadButton(refAndName)} </div> </Dropzone> ); @@ -80,25 +78,32 @@ class SoftwareProductComponentsMonitoringView extends Component { renderUploadButton(refAndName) { let {isReadOnlyMode} = this.props; return ( - <div - className={`software-product-landing-view-top-block-col-upl${isReadOnlyMode ? ' disabled' : ''}`}> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' data-test-id={`monitoring-upload-${refAndName}`} onClick={() => this.refs[refAndName].open()}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> + <DraggableUploadFileBox + dataTestId={`monitoring-upload-${refAndName}`} + className='software-product-landing-view-top-block-col-upl' + isReadOnlyMode={isReadOnlyMode} + onClick={() => this.refs[refAndName].open()}/> ); } - renderUploadedFileName(filename, type) { + renderUploadedFileName(filename, type, isReadOnlyMode) { return ( - <ButtonToolbar> - <ButtonGroup> - <Button disabled>{filename}</Button> - <Button className='delete-button' onClick={()=>this.props.onDeleteFile(type)}>X</Button> - </ButtonGroup> - </ButtonToolbar> + <div className='monitoring-file'> + <Button + color='white' + disabled={true} + className='filename'> + {filename} + </Button> + + <Button + color='gray' + data-test-id={`monitoring-delete-${type}`} + disabled={isReadOnlyMode} + onClick={()=>this.props.onDeleteFile(type)} + iconName='close' + className='delete'/> + </div> ); } diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx index 5a159b4a87..4d460c722d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -122,7 +122,7 @@ class SoftwareProductComponentsNetworkView extends React.Component { <ListEditorItemViewField> <div className={isManual ? 'details-col' : 'details'}> <div className={isManual ? 'manual-title' : 'title'}>{i18n('Purpose of NIC')}</div> - <div className={isManual ? 'description' : ''}>{description ? description : i18n('N/A')}</div> + {description && <div className={isManual ? 'description' : ''}>{description}</div>} </div> {!isManual && <div className='details'> <div className='title'>{i18n('Network')}</div> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx index 18f2ee129c..87abaf4978 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx @@ -17,6 +17,7 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Dropzone from 'react-dropzone'; +import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx'; import {optionsInputValues as ComponentProcessesOptionsInputValues} from './SoftwareProductComponentProcessesConstants.js'; import Form from 'nfvo-components/input/validation/Form.jsx'; import Input from 'nfvo-components/input/validation/Input.jsx'; @@ -31,17 +32,6 @@ const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ type: React.PropTypes.string }); -const FileUploadBox = ({onClick}) => { - return ( - <div className='file-upload-box'> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={onClick}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> - ); -}; class SoftwareProductProcessesEditorView extends React.Component { @@ -99,7 +89,7 @@ class SoftwareProductProcessesEditorView extends React.Component { type='text'/> </GridItem> <GridItem colSpan={2}> - <FileUploadBox onClick={() => this.refs.processEditorFileInput.open()} /> + <DraggableUploadFileBox isReadOnlyMode={isReadOnlyMode} className='file-upload-box' onClick={() => this.refs.processEditorFileInput.open()} /> </GridItem> </GridSection> <GridSection> diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js index 18a3b1e8ff..7149adbbfb 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js @@ -33,14 +33,15 @@ const mapStateToProps = ({softwareProduct}) => { qdata, isReadOnlyMode, qGenericFieldInfo, - dataMap + dataMap, + version: currentVSP.version }; }; -const mapActionToProps = (dispatch, {softwareProductId, version, componentId}) => { +const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { return { onQDataChanged: (deltaData) => ValidationHelper.qDataChanged(dispatch, {deltaData, qName: COMPONENTS_QUESTIONNAIRE}), - onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + onSubmit: ({componentData, qdata, version}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, {softwareProductId, version, vspComponentId: componentId, componentData, qdata}); } }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx index 28bdf8e5e5..00df21bb59 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx @@ -162,7 +162,7 @@ class SoftwareProductComponentStorageView extends React.Component { }; render() { - let {onQDataChanged, dataMap, qGenericFieldInfo, isReadOnlyMode, onSubmit, qdata} = this.props; + let {onQDataChanged, dataMap, qGenericFieldInfo, isReadOnlyMode, onSubmit, qdata, version} = this.props; return( <div className='vsp-component-questionnaire-view'> @@ -170,7 +170,7 @@ class SoftwareProductComponentStorageView extends React.Component { ref={form => this.form = form } isValid={true} formReady={null} - onSubmit={() => onSubmit({qdata})} + onSubmit={() => onSubmit({qdata, version})} className='component-questionnaire-validation-form' isReadOnlyMode={isReadOnlyMode} hasButtons={false}> @@ -182,8 +182,9 @@ class SoftwareProductComponentStorageView extends React.Component { ); } - save(){ - return this.form.handleFormSubmit(new Event('dummy')); + save(){ + const {componentData, qdata, onSubmit, version} = this.props; + return onSubmit({componentData, qdata, version}); } } 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 19e2d5b0db..3b973c65cd 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js @@ -27,7 +27,7 @@ export const mapStateToProps = ({finalizedLicenseModelList, softwareProductList, let VSPNames = {}; for (let i = 0; i < softwareProductList.length; i++) { - VSPNames[softwareProductList[i].name] = softwareProductList[i].id; + VSPNames[softwareProductList[i].name.toLowerCase()] = softwareProductList[i].id; } return { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx index 2e0cd340de..a427470a4f 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/dependencies/SoftwareProductDependenciesView.jsx @@ -63,14 +63,15 @@ export default class SoftwareProductDependenciesView extends React.Component { numOfIcons={2} isReadOnlyMode={isReadOnlyMode} onAdd={canAdd ? onAddDependency : undefined} - onAddItem={i18n('Add Rule')}> + onAddItem={i18n('Add Rule')}> {softwareProductDependencies.map(dependency => ( <SelectActionTableRow key={dependency.id} onDelete={() => onDataChanged(softwareProductDependencies.filter(currentDependency => currentDependency.id !== dependency.id))} overlayMsg={i18n('There is a loop between selections')} hasError={dependency.hasCycle} - hasErrorIndication> + hasErrorIndication + showDelete={dependency.id !== 'fake' || dependency.hasCycle !== undefined}> <SelectActionTableCell options={this.filterSources({componentsOptions, sourceToTargetMapping, selectedSourceId: dependency.sourceId, selectedTargetId: dependency.targetId})} selected={dependency.sourceId} diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js index 6b924a2816..ba00d4e56e 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js @@ -58,7 +58,7 @@ export function mapStateToProps({licenseModel, softwareProduct}) { let DFNames = {}; deploymentFlavors.map(deployment => { - DFNames[deployment.model] = deployment.id; + DFNames[deployment.model.toLowerCase()] = deployment.id; }); return { diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx index 98dd7730bd..e4caf92c21 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx @@ -19,7 +19,7 @@ import i18n from 'nfvo-utils/i18n/i18n.js'; import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js'; import Form from 'nfvo-components/input/validation/Form.jsx'; import Input from 'nfvo-components/input/validation/Input.jsx'; -import InputOptions from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx'; import GridSection from 'nfvo-components/grid/GridSection.jsx'; import GridItem from 'nfvo-components/grid/GridItem.jsx'; import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx index d3738e3ea4..7ffbeda4dc 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -19,7 +19,7 @@ import Dropzone from 'react-dropzone'; import i18n from 'nfvo-utils/i18n/i18n.js'; - +import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import SoftwareProductComponentsList from '../components/SoftwareProductComponentsList.js'; @@ -128,14 +128,11 @@ class SoftwareProductLandingPageView extends React.Component { </div> </div> </div> - <div - className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})}> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div data-test-id='upload-btn' className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> + <DraggableUploadFileBox + dataTestId='upload-btn' + isReadOnlyMode={isReadOnlyMode} + className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})} + onClick={() => this.refs.fileInput.open()}/> </div> </div> ); @@ -229,7 +226,7 @@ const ProductSummary = ({currentSoftwareProduct, onDetailsSelect}) => { const LicenseAgreement = ({licenseAgreementName}) => { if (!licenseAgreementName) { - return (<div className='missing-license'><SVGIcon name='exclamationTriangleFull'/><div className='warning-text'>{i18n('Missing')}</div></div>); + return (<div className='missing-license'><SVGIcon color='warning' name='exclamationTriangleFull'/><div className='warning-text'>{i18n('Missing')}</div></div>); } return <div>{licenseAgreementName}</div>; }; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx index 137e4a2b4e..0df36cf65d 100644 --- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx @@ -17,6 +17,7 @@ import React from 'react'; import Dropzone from 'react-dropzone'; import classnames from 'classnames'; +import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx'; import i18n from 'nfvo-utils/i18n/i18n.js'; import {optionsInputValues as ProcessesOptionsInputValues} from './SoftwareProductProcessesConstants.js'; import Form from 'nfvo-components/input/validation/Form.jsx'; @@ -32,17 +33,6 @@ const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ type: React.PropTypes.string }); -const FileUploadBox = ({onClick}) => { - return ( - <div className='file-upload-box'> - <div className='drag-text'>{i18n('Drag & drop for upload')}</div> - <div className='or-text'>{i18n('or')}</div> - <div className='upload-btn primary-btn' onClick={onClick}> - <span className='primary-btn-text'>{i18n('Select file')}</span> - </div> - </div> - ); -}; class SoftwareProductProcessesEditorView extends React.Component { @@ -101,7 +91,7 @@ class SoftwareProductProcessesEditorView extends React.Component { type='text'/> </GridItem> <GridItem colSpan={2}> - <FileUploadBox onClick={() => this.refs.processEditorFileInput.open()}/> + <DraggableUploadFileBox isReadOnlyMode={isReadOnlyMode} className='file-upload-box' onClick={() => this.refs.processEditorFileInput.open()}/> </GridItem> </GridSection> <GridSection> diff --git a/openecomp-ui/test-utils/MockSVGIcon.js b/openecomp-ui/test-utils/MockSVGIcon.js new file mode 100644 index 0000000000..6ce76cf91a --- /dev/null +++ b/openecomp-ui/test-utils/MockSVGIcon.js @@ -0,0 +1,17 @@ +import React from 'react'; +//import ReactDOMServer from 'react-dom/server'; + +const SVGIcon = ({name, onClick, label, className, iconClassName, labelClassName, labelPosition, color, disabled, ...other}) => { + let colorClass = (color !== '') ? '__' + color : ''; + let classes = `svg-icon-wrapper ${iconClassName} ${className} ${colorClass} ${onClick ? 'clickable' : ''} ${disabled ? 'disabled' : ''} ${labelPosition}`; + + let iconMock = ( + <div {...other} onClick={onClick} className={classes}> + <span className={`svg-icon __${name} ${disabled ? 'disabled' : ''}`} /> + {label && <span className={`svg-icon-label ${labelClassName}`}>{label}</span>} + </div> + ); +// console.log(ReactDOMServer.renderToStaticMarkup(iconMock)); + return iconMock; +}; +export default SVGIcon; diff --git a/openecomp-ui/test-utils/factories/licenseModel/EntitlementPoolFactories.js b/openecomp-ui/test-utils/factories/licenseModel/EntitlementPoolFactories.js index 14df58e4c2..a28245e8bc 100644 --- a/openecomp-ui/test-utils/factories/licenseModel/EntitlementPoolFactories.js +++ b/openecomp-ui/test-utils/factories/licenseModel/EntitlementPoolFactories.js @@ -19,9 +19,7 @@ import {overviewEditorHeaders} from 'sdc-app/onboarding/licenseModel/overview/Li Factory.define('EntitlementPoolBaseFactory') .attrs({ name: 'EntitlementPoolName', - description: 'description', - entitlementMetric: {'choice': 'User', 'other': ''}, - manufacturerReferenceNumber: '123' + description: 'description' }); Factory.define('EntitlementPoolExtendedBaseFactory') @@ -29,10 +27,10 @@ Factory.define('EntitlementPoolExtendedBaseFactory') .attrs({ thresholdValue: 75, thresholdUnits: '%', - increments: 'string', - aggregationFunction: {'choice': 'Average', 'other': ''}, - operationalScope: {'choices': ['Other'], 'other': 'blabla'}, - time: {'choice': 'Hour', 'other': ''} + increments: 'string', + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + startDate: (new Date()).getTime(), + expiryDate: (new Date()).getTime() }); export const EntitlementPoolListItemFactory = new Factory() diff --git a/openecomp-ui/test-utils/factories/licenseModel/LimitFactories.js b/openecomp-ui/test-utils/factories/licenseModel/LimitFactories.js new file mode 100644 index 0000000000..5ad8b12228 --- /dev/null +++ b/openecomp-ui/test-utils/factories/licenseModel/LimitFactories.js @@ -0,0 +1,38 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {Factory} from 'rosie'; +import {limitType} from 'sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js'; +import IdMixin from 'test-utils/factories/mixins/IdMixin.js'; + +Factory.define('LimitBaseFactory') + .attrs({ + name: 'SpLimit1', + type: limitType.SERVICE_PROVIDER, + description: 'fgfg', + value: 45, + aggregationFunction: 'Peak', + time: 'Day' + }); + +export const LimitPostFactory = new Factory() + .attrs({metric: {choice: 'BWDT', other: ''}, unit: {choice: 'GB', other: ''}}) + .extend('LimitBaseFactory'); + +export const LimitItemFactory = new Factory() + .attrs({metric: 'BWDT', unit: 'GB'}) + .extend('LimitBaseFactory') + .extend(IdMixin); diff --git a/openecomp-ui/test/licenseModel/entitlementPools/test.js b/openecomp-ui/test/licenseModel/entitlementPools/test.js index 15e1deecd6..f5415239ad 100644 --- a/openecomp-ui/test/licenseModel/entitlementPools/test.js +++ b/openecomp-ui/test/licenseModel/entitlementPools/test.js @@ -20,6 +20,8 @@ import {storeCreator} from 'sdc-app/AppStore.js'; import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; import {EntitlementPoolStoreFactory, EntitlementPoolPostFactory} from 'test-utils/factories/licenseModel/EntitlementPoolFactories.js'; import VersionControllerUtilsFactory from 'test-utils/factories/softwareProduct/VersionControllerUtilsFactory.js'; +import {LimitItemFactory, LimitPostFactory} from 'test-utils/factories/licenseModel/LimitFactories.js'; +import {getStrValue} from 'nfvo-utils/getValue.js'; describe('Entitlement Pools Module Tests', function () { @@ -162,4 +164,181 @@ describe('Entitlement Pools Module Tests', function () { }); }); + it('Load Limits List', () => { + + const limitsList = LimitItemFactory.buildList(3); + deepFreeze(limitsList); + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolEditor.limitsList', limitsList); + const entitlementPool = EntitlementPoolStoreFactory.build(); + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: limitsList}; + }); + + return EntitlementPoolsActionHelper.fetchLimits(store.dispatch, {licenseModelId: LICENSE_MODEL_ID, version, entitlementPool}).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Add Limit', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const limitToAdd = LimitPostFactory.build(); + let limitFromBE = {...limitToAdd}; + limitFromBE.metric = getStrValue(limitFromBE.metric); + limitFromBE.unit = getStrValue(limitFromBE.unit); + + deepFreeze(limitToAdd); + deepFreeze(limitFromBE); + + const LimitIdFromResponse = 'ADDED_ID'; + const limitAddedItem = {...limitToAdd, id: LimitIdFromResponse}; + deepFreeze(limitAddedItem); + const entitlementPool = EntitlementPoolStoreFactory.build(); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolEditor.limitsList', [limitAddedItem]); + + mockRest.addHandler('post', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits`); + expect(data).toEqual(limitFromBE); + expect(options).toEqual(undefined); + return { + returnCode: 'OK', + value: LimitIdFromResponse + }; + }); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: [limitAddedItem]}; + }); + + return EntitlementPoolsActionHelper.submitLimit(store.dispatch, + { + licenseModelId: LICENSE_MODEL_ID, + version, + entitlementPool, + limit: limitToAdd + } + ).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + + it('Delete Limit', () => { + + const limitsList = LimitItemFactory.buildList(1); + deepFreeze(limitsList); + + const store = storeCreator({ + licenseModel: { + entitlementPool: { + entitlementPoolEditor: { + limitsList + } + } + } + }); + deepFreeze(store.getState()); + + const entitlementPool = EntitlementPoolStoreFactory.build(); + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolEditor.limitsList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits/${limitsList[0].id}`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return { + results: { + returnCode: 'OK' + } + }; + }); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: []}; + }); + + return EntitlementPoolsActionHelper.deleteLimit(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + version, + entitlementPool, + limit: limitsList[0] + }).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Update Limit', () => { + + const limitsList = LimitItemFactory.buildList(1); + deepFreeze(limitsList); + const entitlementPool = EntitlementPoolStoreFactory.build(); + const store = storeCreator({ + licenseModel: { + entitlementPool: { + entitlementPoolEditor: { + limitsList + } + } + } + }); + + deepFreeze(store.getState()); + + + const previousData = limitsList[0]; + + deepFreeze(previousData); + const limitId = limitsList[0].id; + + let updatedLimit = {...previousData, name: 'updatedLimit'}; + + const updatedLimitForPut = {...updatedLimit, id: undefined}; + updatedLimit.metric = {choice: updatedLimit.metric, other: ''}; + updatedLimit.unit = {choice: updatedLimit.unit, other: ''}; + deepFreeze(updatedLimit); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolEditor.limitsList', [updatedLimitForPut]); + + + mockRest.addHandler('put', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits/${limitId}`); + expect(data).toEqual(updatedLimitForPut); + expect(options).toEqual(undefined); + return {returnCode: 'OK'}; + }); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/entitlement-pools/${entitlementPool.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: [updatedLimitForPut]}; + }); + + return EntitlementPoolsActionHelper.submitLimit(store.dispatch, + { + licenseModelId: LICENSE_MODEL_ID, + version, + entitlementPool, + limit: updatedLimit + } + ).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + }); diff --git a/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js b/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js index dd09030f4f..739e266d7f 100644 --- a/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js +++ b/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js @@ -21,6 +21,8 @@ import {LicenseKeyGroupStoreFactory, LicenseKeyGroupPostFactory} from 'test-util import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; import VersionControllerUtilsFactory from 'test-utils/factories/softwareProduct/VersionControllerUtilsFactory.js'; +import {LimitItemFactory, LimitPostFactory} from 'test-utils/factories/licenseModel/LimitFactories.js'; +import {getStrValue} from 'nfvo-utils/getValue.js'; describe('License Key Groups Module Tests', function () { @@ -157,4 +159,178 @@ describe('License Key Groups Module Tests', function () { }); }); + it('Load Limits List', () => { + + const limitsList = LimitItemFactory.buildList(3); + deepFreeze(limitsList); + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsEditor.limitsList', limitsList); + const licenseKeyGroup = LicenseKeyGroupStoreFactory.build(); + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: limitsList}; + }); + + return LicenseKeyGroupsActionHelper.fetchLimits(store.dispatch, {licenseModelId: LICENSE_MODEL_ID, version, licenseKeyGroup}).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Add Limit', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const limitToAdd = LimitPostFactory.build(); + let limitFromBE = {...limitToAdd}; + limitFromBE.metric = getStrValue(limitFromBE.metric); + limitFromBE.unit = getStrValue(limitFromBE.unit); + + deepFreeze(limitToAdd); + deepFreeze(limitFromBE); + + const LimitIdFromResponse = 'ADDED_ID'; + const limitAddedItem = {...limitToAdd, id: LimitIdFromResponse}; + deepFreeze(limitAddedItem); + const licenseKeyGroup = LicenseKeyGroupStoreFactory.build(); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsEditor.limitsList', [limitAddedItem]); + + mockRest.addHandler('post', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits`); + expect(data).toEqual(limitFromBE); + expect(options).toEqual(undefined); + return { + returnCode: 'OK', + value: LimitIdFromResponse + }; + }); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: [limitAddedItem]}; + }); + + return LicenseKeyGroupsActionHelper.submitLimit(store.dispatch, + { + licenseModelId: LICENSE_MODEL_ID, + version, + licenseKeyGroup, + limit: limitToAdd + } + ).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Delete Limit', () => { + + const limitsList = LimitItemFactory.buildList(1); + deepFreeze(limitsList); + + const store = storeCreator({ + licenseModel: { + entitlementPool: { + entitlementPoolEditor: { + limitsList + } + } + } + }); + deepFreeze(store.getState()); + + const licenseKeyGroup = LicenseKeyGroupStoreFactory.build(); + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsEditor.limitsList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits/${limitsList[0].id}`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return { + results: { + returnCode: 'OK' + } + }; + }); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: []}; + }); + + return LicenseKeyGroupsActionHelper.deleteLimit(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + version, + licenseKeyGroup, + limit: limitsList[0] + }).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Update Limit', () => { + + const limitsList = LimitItemFactory.buildList(1); + deepFreeze(limitsList); + const licenseKeyGroup = LicenseKeyGroupStoreFactory.build(); + const store = storeCreator({ + licenseModel: { + licenseKeyGroup: { + licenseKeyGroupsEditor: { + limitsList + } + } + } + }); + + deepFreeze(store.getState()); + + + const previousData = limitsList[0]; + deepFreeze(previousData); + const limitId = limitsList[0].id; + + let updatedLimit = {...previousData, name: 'updatedLimit'}; + const updatedLimitForPut = {...updatedLimit, id: undefined}; + updatedLimit.metric = {choice: updatedLimit.metric, other: ''}; + updatedLimit.unit = {choice: updatedLimit.unit, other: ''}; + deepFreeze(updatedLimit); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsEditor.limitsList', [updatedLimitForPut]); + + + mockRest.addHandler('put', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits/${limitId}`); + expect(data).toEqual(updatedLimitForPut); + expect(options).toEqual(undefined); + return {returnCode: 'OK'}; + }); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/versions/${version.id}/license-key-groups/${licenseKeyGroup.id}/limits`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return {results: [updatedLimitForPut]}; + }); + + return LicenseKeyGroupsActionHelper.submitLimit(store.dispatch, + { + licenseModelId: LICENSE_MODEL_ID, + version, + licenseKeyGroup, + limit: updatedLimit + } + ).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + }); diff --git a/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js b/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js index 029ea31889..b1127386f7 100644 --- a/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js +++ b/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js @@ -40,7 +40,7 @@ describe('listEditor Module Tests', function () { </ListEditorView> ); expect(itemView).toBeTruthy(); - let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, 'sliders'); + let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, '__sliders'); TestUtils.Simulate.click(sliderIcon); }); @@ -60,7 +60,7 @@ describe('listEditor Module Tests', function () { </ListEditorItemView> ); expect(itemView).toBeTruthy(); - let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, 'sliders'); + let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, '__sliders'); TestUtils.Simulate.click(sliderIcon); }); @@ -71,7 +71,7 @@ describe('listEditor Module Tests', function () { </ListEditorItemView> ); expect(itemView).toBeTruthy(); - let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, 'trashO'); + let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, '__trashO'); TestUtils.Simulate.click(sliderIcon); }); @@ -82,7 +82,8 @@ describe('listEditor Module Tests', function () { </ListEditorItemView> ); expect(itemView).toBeTruthy(); - let trashIcon = TestUtils.scryRenderedDOMComponentsWithClass(itemView, 'fa-trash-o'); + let trashIcon = TestUtils.scryRenderedDOMComponentsWithClass(itemView, '__trashOq'); expect(trashIcon).toEqual([]); }); + }); diff --git a/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js b/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js index 7d4d57eb35..e14e9b76f5 100644 --- a/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js +++ b/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js @@ -16,7 +16,7 @@ import React from 'react'; - +import ReactDOMServer from 'react-dom/server'; import TestUtils from 'react-addons-test-utils'; import {mount} from 'enzyme'; import VersionController from 'nfvo-components/panel/versionController/VersionController.jsx'; @@ -131,21 +131,21 @@ describe('versionController UI Component', () => { let callVCActionProps = { ...props, version: '1.0', callVCAction: function(){} }; let versionController = mount(<VersionController isCheckedOut={true} status={statusEnum.CHECK_OUT_STATUS} {...callVCActionProps} />); let elem = versionController.find('[data-test-id="vc-checkout-btn"]'); - let svgIcon = versionController.find('.versionControllerLockClosed'); + expect(elem).toBeTruthy(); expect(elem.length).toEqual(1); - expect(svgIcon.hasClass('disabled')).toBe(true); + expect(elem.find('.svg-icon').length).toEqual(1); + expect(elem.find('.svg-icon').hasClass('disabled')).toBe(true); }); it('Doesn\'t show the checkout button', () => { let callVCActionProps = { ...props, version: '1.0', callVCAction: function(){} }; let versionController = mount(<VersionController isCheckedOut={false} status={statusEnum.CHECK_IN_STATUS} {...callVCActionProps} />); let elem = versionController.find('[data-test-id="vc-checkout-btn"]'); - let svgIcon = versionController.find('.versionControllerLockClosed'); - expect(elem).toBeTruthy(); expect(elem.length).toBe(1); - expect(svgIcon.hasClass('disabled')).toBe(true); + expect(elem.find('.svg-icon').length).toEqual(1); + expect(elem.find('.svg-icon').hasClass('disabled')).toBe(true); }); |