From: Michael Lando Date: Sun, 19 Feb 2017 10:57:33 +0000 (+0200) Subject: Add new code new version X-Git-Tag: 1.0.0-Amsterdam~309 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=efa037d34be7b1570efdc767c79fad8d4005f10e;p=sdc.git Add new code new version Change-Id: Ic02a76313503b526f17c3df29eb387a29fe6a42a Signed-off-by: Michael Lando --- diff --git a/.gitignore b/.gitignore index da39ab03ca..9a8035e9fd 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,7 @@ asdc-tests/test-output/** asdc-chef/cookbooks/Deploy-SDandC/attributes/version.rb asdc-tests/ExtentReport/ASDC_CI_Extent_Report.html + +**/*.log +sdctool.tar +asdc-tests.tar diff --git a/dox-sequence-diagram-ui/.babelrc b/dox-sequence-diagram-ui/.babelrc new file mode 100644 index 0000000000..91f0a7318b --- /dev/null +++ b/dox-sequence-diagram-ui/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "react", "stage-0"] +} diff --git a/dox-sequence-diagram-ui/.gitignore b/dox-sequence-diagram-ui/.gitignore new file mode 100644 index 0000000000..3ad9501b01 --- /dev/null +++ b/dox-sequence-diagram-ui/.gitignore @@ -0,0 +1,6 @@ + +.idea +npm-debug.log +dist +node_modules +.npmrc diff --git a/dox-sequence-diagram-ui/.npmignore b/dox-sequence-diagram-ui/.npmignore new file mode 100644 index 0000000000..723ef36f4e --- /dev/null +++ b/dox-sequence-diagram-ui/.npmignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/dox-sequence-diagram-ui/LICENSE.txt b/dox-sequence-diagram-ui/LICENSE.txt new file mode 100644 index 0000000000..2507db8a57 --- /dev/null +++ b/dox-sequence-diagram-ui/LICENSE.txt @@ -0,0 +1 @@ +(c) Copyright 2016 ECOMP, all rights reserved. diff --git a/dox-sequence-diagram-ui/eslintrc.json b/dox-sequence-diagram-ui/eslintrc.json new file mode 100644 index 0000000000..b2a3f24aaa --- /dev/null +++ b/dox-sequence-diagram-ui/eslintrc.json @@ -0,0 +1,32 @@ +{ + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "env": { + "browser": true + }, + "plugins": [ + "react" + ], + "extends": [ + "airbnb" + ], + "rules": { + "padded-blocks": 0, + "max-len": ["error", 160, 4], + "no-underscore-dangle": 0, + "global-require": 0, + "react/sort-comp": 0, + "new-cap": 0 + }, + "settings": { + "react": { + "pragma": "React", + "version": "0.14.8" + } + } +} diff --git a/dox-sequence-diagram-ui/package.json b/dox-sequence-diagram-ui/package.json new file mode 100644 index 0000000000..c53d6d0ebc --- /dev/null +++ b/dox-sequence-diagram-ui/package.json @@ -0,0 +1,63 @@ +{ + "name": "dox-sequence-diagram-ui", + "version": "1.0.0", + "description": "", + "main": "dist/index.js", + "devDependencies": { + "babel-core": "^6.2.4", + "babel-eslint": "^6.0.4", + "babel-loader": "^6.2.4", + "babel-plugin-lodash": "^3.1.4", + "babel-polyfill": "^6.5.0", + "babel-preset-es2015": "^6.3.13", + "babel-preset-react": "^6.5.0", + "babel-preset-stage-0": "^6.5.0", + "babelify": "^7.2.0", + "copy": "^0.2.0", + "css-loader": "^0.23.1", + "ejs-html-loader": "^1.2.2", + "eslint": "^2.10.2", + "eslint-config-airbnb": "^9.0.1", + "eslint-loader": "^1.3.0", + "eslint-plugin-import": "^1.8.0", + "eslint-plugin-jsx-a11y": "^1.2.2", + "eslint-plugin-react": "^5.1.1", + "file-loader": "^0.8.5", + "json-loader": "^0.5.4", + "node-http-proxy": "^0.2.3", + "node-sass": "^3.7.0", + "path": "^0.12.7", + "raw-loader": "^0.5.1", + "redux-devtools": "^3.3.1", + "sass-loader": "^3.2.0", + "style-loader": "^0.13.1", + "svg-sprite-loader": "0.0.19", + "url-loader": "^0.5.7", + "webpack": "^1.13.1", + "webpack-dev-server": "^1.14.1" + }, + "author": "ECOMP", + "license": "SEE LICENSE IN LICENSE.TXT", + "dependencies": { + "d3": "^3.5.16", + "lodash": "^4.12.0", + "react": "^15.1.0", + "react-dnd": "^2.1.2", + "react-dnd-html5-backend": "^2.1.2", + "react-dom": "^15.1.0", + "react-redux": "^4.4.5", + "react-select": "^1.0.0-beta13", + "redux": "^3.5.2" + }, + "engines": { + "node": ">=5.1", + "npm": ">=3.3" + }, + "scripts": { + "prepublish": "npm run build", + "buildbabel": "babel src -d lib && node tools/copy-assets", + "build": "webpack", + "start": "webpack-dev-server", + "lint": "eslint . --ext .js --ext .jsx --cache" + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/WEB-INF/web.xml b/dox-sequence-diagram-ui/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..a2f51b5271 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,14 @@ + + + + Ecomp ES6 Blueprint + + + index.html + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/favicon.ico b/dox-sequence-diagram-ui/src/main/webapp/favicon.ico new file mode 100644 index 0000000000..a9ee1b6351 Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/favicon.ico differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/index.html b/dox-sequence-diagram-ui/src/main/webapp/index.html new file mode 100644 index 0000000000..b4a3a73dda --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/index.html @@ -0,0 +1,13 @@ + + + + SDC Sequencer - DEMO + + + + + + +
+ + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx new file mode 100644 index 0000000000..ff8e9a22ca --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx @@ -0,0 +1,199 @@ +/*! + * 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 Application from './components/application/Application'; +import Common from './common/Common'; +import Options from './common/Options'; +import Model from './model/Model'; +import Metamodel from './model/Metamodel'; +import Metamodels from './model/Metamodels'; +import Scenarios from './model/demo/scenarios/Scenarios'; +import '../../../../res/sdc-sequencer.scss'; +/** + * ASDC Sequencer entry point. + */ +export default class Sequencer extends React.Component { + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + constructor(props, context) { + super(props, context); + + + this.setMetamodel.bind(this); + this.setModel.bind(this); + this.getModel.bind(this); + this.getMetamodel.bind(this); + this.getSVG.bind(this); + this.getDemoScenarios.bind(this); + this.newModel.bind(this); + + // Parse options. + + this.options = new Options(props.options); + + // Default scenarios. + + const scenarios = this.getDemoScenarios(); + this.setMetamodel(scenarios.getMetamodels()); + + // this.setModel(scenarios.getBlank()); + this.setModel(scenarios.getDimensions()); + // this.setModel(scenarios.getECOMP()); + + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Optionally save known metamodels so that subsequent loading and unloading + * of models needn't include the corresponding metamodel. + * @param metamodels array of conformant metamodel JSON definitions. + * @return this. + */ + setMetamodel(metamodels) { + Common.assertType(metamodels, 'Array'); + this.metamodels = new Metamodels(metamodels); + return this; + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set current diagram. + * @param modelJSON JSON diagram spec. + * @param metamodelIdOrDefinition optional metamodel definition or reference. Defaults to + * the model's metadata @ref, or the default (permissive) metamodel. + * @return this. + */ + setModel(modelJSON, metamodelIdOrDefinition) { + Common.assertType(modelJSON, 'Object'); + const ref = (modelJSON.metadata) ? modelJSON.metadata.ref : undefined; + const metamodel = this.getMetamodel(metamodelIdOrDefinition || ref); + Common.assertInstanceOf(metamodel, Metamodel); + this.model = new Model(modelJSON, metamodel); + if (this.application) { + this.application.setModel(this.model); + } + return this; + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get current diagram state. At any given instant the diagram might not make *sense* + * but it should always be syntactically valid. + * @return current Model. + */ + getModel() { + + if (this.application) { + const model = this.application.getModel(); + if (model) { + return model.unwrap(); + } + } + + return this.model; + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Extract SVG element. + * @return stringified SVG element. + */ + getSVG() { + return this.application.getSVG(); + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get demo scenarios, allowing initialization in demo mode from the outside. + * @returns {Scenarios} + */ + getDemoScenarios() { + return new Scenarios(); + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Create new model. + * @param metamodelIdOrDefinition + * @return newly-created model. + */ + newModel(metamodelIdOrDefinition) { + const metamodel = this.getMetamodel(metamodelIdOrDefinition); + Common.assertInstanceOf(metamodel, Metamodel); + const model = new Model({}, metamodel); + if (this.application) { + this.application.setModel(model); + } + return model; + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get Metamodel instance corresponding to an ID or JSON definition. + * @param metamodelIdOrDefinition String ID or JSON definition. + * @returns Metamodel instance. + * @private + */ + getMetamodel(metamodelIdOrDefinition) { + const metamodelType = Common.getType(metamodelIdOrDefinition); + if (metamodelType === 'Object') { + return new Metamodel(metamodelIdOrDefinition); + } + return this.metamodels.getMetamodelOrDefault(metamodelIdOrDefinition); + } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render current diagram state. + */ + render() { + + if (this.props.model) { + + // If a model was specified as a property, apply it. Otherwise + // fall back to the demo model. + + const scenarios = this.getDemoScenarios(); + const metamodel = [scenarios.getBlankMetamodel(), scenarios.getECOMPMetamodel()]; + if (this.props.metamodel) { + metamodel.push(this.props.metamodel); + } + this.setMetamodel(metamodel); + this.setModel(this.props.model); + } + + return ( + { this.application = a; }} /> + ); + } + +} + +Sequencer.propTypes = { + options: React.PropTypes.object.isRequired, + model: React.PropTypes.object, + metamodel: React.PropTypes.object, +}; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js new file mode 100644 index 0000000000..7337367dca --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js @@ -0,0 +1,356 @@ +/*! + * 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. + */ + +/** + * Common operations. + */ +export default class Common { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Retrieve and start a simple timer. Retrieve elapsed time by calling #ms(). + * @returns {*} + */ + static timer() { + const start = new Date().getTime(); + return { + ms() { + return (new Date().getTime() - start); + }, + }; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get datatype, stripping '[object Boolean]' to just 'Boolean'. + * @param o JS object. + * @return String like String, Number, Date, Null, Undefined, stuff like that. + */ + static getType(o) { + const str = Object.prototype.toString.call(o); + const prefix = '[object '; + if (str.substr(str, prefix.length) === prefix) { + return str.substr(prefix.length, str.length - (prefix.length + 1)); + } + return str; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert that an argument was provided. + * @param value to be checked. + * @param message message on assertion failure. + * @return value. + */ + static assertNotNull(value, message = 'Unexpected null value') { + if (!value) { + throw new Error(message); + } + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument type. + * @param value to be checked. + * @param expected expected type string, e,g. Number from [object Number]. + * @return value. + */ + static assertType(value, expected) { + const type = this.getType(value); + if (type !== expected) { + throw new Error(`Expected type ${expected}, got ${type}`); + } + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument type. + * @param value to be checked. + * @param unexpected unexpected type string, e,g. Number from [object Number]. + * @return value. + */ + static assertNotType(value, unexpected) { + const type = this.getType(value); + if (type === unexpected) { + throw new Error(`Forbidden type "${unexpected}"`); + } + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument is a simple JSON object, and specifically not (something like an) ES6 class. + * @param value to be checked. + * @return value. + */ + static assertPlainObject(value) { + Common.assertType(value, 'Object'); + // TODO + /* + if (!($.isPlainObject(value))) { + throw new Error(`Expected plain object: ${value}`); + } + */ + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument type. + * @param value to be checked. + * @param c expected class. + * @return value. + */ + static assertInstanceOf(value, c) { + Common.assertNotNull(value); + if (!(value instanceof c)) { + throw new Error(`Expected instanceof ${c}: ${value}`); + } + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert that a string matches a regex. + * @param value value to be tested. + * @param re pattern to be applied. + * @return value. + */ + static assertMatches(value, re) { + this.assertType(value, 'String'); + this.assertType(re, 'RegExp'); + if (!re.test(value)) { + throw new Error(`Value ${value} doesn't match pattern ${re}`); + } + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert the value of a boolean. + * + * @param bool to be checked. + * @param message optional message on assertion failure. + * @return value. + */ + static assertThat(bool, message) { + if (!bool) { + throw new Error(message || `Unexpected: ${bool}`); + } + return bool; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Verify that a value, generally a function arg, is a DOM element. + * @param value to be checked. + * @return value. + */ + static assertHTMLElement(value) { + if (!Common.isHTMLElement(value)) { + throw new Error(`Expected HTMLElement: ${value}`); + } + return value; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Check whether a value, generally a function arg, is an HTML DOM element. + * @param o to be checked. + * @return true if DOM element. + */ + static isHTMLElement(o) { + if (typeof HTMLElement === 'object') { + return o instanceof HTMLElement; + } + return o && typeof o === 'object' && o !== null + && o.nodeType === 1 && typeof o.nodeName === 'string'; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Check if a string is non-empty. + * @param s string to be checked. + * @returns false if non-blank string, true otherwise. + */ + static isBlank(s) { + if (Common.getType(s) === 'String') { + return (s.trim().length === 0); + } + return true; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Detect dates that are numbers, milli/seconds since epoch.. + * + * @param n candidate number. + * @returns {boolean} + */ + static isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Parse the text output from a template to a DOM element. + * @param txt input text. + * @returns {Element} + */ + static txt2dom(txt) { + return new DOMParser().parseFromString(txt, 'image/svg+xml').documentElement; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Recursively convert a DOM element to an SVG (namespaced) element. Otherwise + * you get HTML elements that *happen* to have SVG names, but which aren't actually SVG. + * + * @param node DOM node to be converted. + * @param svg to be updated. + * @returns {*} for chaining. + */ + static dom2svg(node, svg) { + + Common.assertNotType(node, 'String'); + + if (node.childNodes && node.childNodes.length > 0) { + + for (const c of node.childNodes) { + switch (c.nodeType) { + case document.TEXT_NODE: + svg.text(c.nodeValue); + break; + default: + break; + } + } + + for (const c of node.childNodes) { + switch (c.nodeType) { + case document.ELEMENT_NODE: + Common.dom2svg(c, svg.append(`svg:${c.nodeName.toLowerCase()}`)); + break; + default: + break; + } + } + } + + if (node.hasAttributes()) { + for (let i = 0; i < node.attributes.length; i++) { + const a = node.attributes.item(i); + svg.attr(a.name, a.value); + } + } + + return svg; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get the lines to be shown in the label. + * + * @param labelText original label text. + * @param wordWrapAt chars at which to break words. + * @param lineWrapAt chars at which to wrap. + * @param maximumLines lines at which to truncate. + * @returns {Array} + */ + static tokenize(labelText = '', wordWrapAt, lineWrapAt, maximumLines) { + + let l = labelText; + + // Hyphenate and break long words. + + const regex = new RegExp(`(\\w{${wordWrapAt - 1}})(?=\\w)`, 'g'); + l = l.replace(regex, '$1- '); + + const labelTokens = l.split(/\s+/); + const lines = []; + let label = ''; + for (const labelToken of labelTokens) { + if (label.length > 0) { + const length = label.length + labelToken.length + 1; + if (length > lineWrapAt) { + lines.push(label.trim()); + label = labelToken; + continue; + } + } + label = `${label} ${labelToken}`; + } + + if (label) { + lines.push(label.trim()); + } + + const truncated = lines.slice(0, maximumLines); + if (truncated.length < lines.length) { + let finalLine = truncated[maximumLines - 1]; + if (finalLine.length > (lineWrapAt - 4)) { + finalLine = finalLine.substring(0, lineWrapAt - 4); + } + finalLine = `${finalLine} ...`; + truncated[maximumLines - 1] = finalLine; + } + + return truncated; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Brutally sanitize an input string. We have no syntax rules, and hence no specific + * rules to apply, but we have very few unconstrained fields, so we can implement a + * crude default and devolve the rest to options. + * @param value value to be sanitized. + * @param options control options including validation rules. + * @param type validation type. + * @returns {*} sanitized string. + * @private + */ + static sanitizeText(value, options, type) { + const rules = Common.assertNotNull(options.validation[type]); + let v = value || rules.defaultValue || ''; + if (rules.replace) { + v = v.replace(rules.replace, ''); + } + if (v.length > rules.maxLength) { + v = `${v.substring(0, rules.maxLength)}...`; + } + return v; + } + +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js new file mode 100644 index 0000000000..187f49bb08 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js @@ -0,0 +1,137 @@ +/*! + * 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. + */ + +/* eslint-disable no-console */ + +import Common from './Common'; + +/** + * Logger, to allow calls to console.log during development, but + * disable them for production. + */ +export default class Logger { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * No-op call so that we can leave imports in place, + * even when there's no debugging. + */ + static noop() { + // Nothing. + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set debug level. + * @param level threshold. + */ + static setLevel(level) { + this.level = Logger.OFF; + if (Common.getType(level) === 'Number') { + this.level = level; + } else { + this.level = Logger[level]; + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get debug level. + * @returns {number|*} + */ + static getLevel() { + return this.level; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write DEBUG-level log. + * @param msg message or tokens. + */ + static debug(...msg) { + if (this.level >= Logger.DEBUG) { + const out = this.serialize(msg); + console.info(`ASDCS [DEBUG] ${out}`); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write INFO-level log. + * @param msg message or tokens. + */ + static info(...msg) { + if (this.level >= Logger.INFO) { + const out = this.serialize(msg); + console.info(`ASDCS [INFO] ${out}`); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write debug. + * @param msg message or tokens. + */ + static warn(msg) { + if (this.level >= Logger.WARN) { + const out = this.serialize(msg); + console.warn(`ASDCS [WARN] ${out}`); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write error. + * @param msg message or tokens. + */ + static error(...msg) { + if (this.level >= Logger.ERROR) { + const out = this.serialize(msg); + console.error(`ASDCS [ERROR] ${out}`); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Serialize msg. + * @param msg message or tokens. + * @returns {string} + */ + static serialize(...msg) { + let out = ''; + msg.forEach((token) => { + out = `${out}${token}`; + }); + return out; + } +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////// + +Logger.OFF = 0; +Logger.ERROR = 1; +Logger.WARN = 2; +Logger.INFO = 3; +Logger.DEBUG = 4; +Logger.level = Logger.OFF; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js new file mode 100644 index 0000000000..15897d7ee3 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js @@ -0,0 +1,136 @@ +/*! + * 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 _merge from 'lodash/merge'; + +import Logger from './Logger'; + +/** + * A wrapper for an options object. User-supplied options are merged with defaults, + * and the result -- runtime options -- are available by calling #getOptions(). + */ +export default class Options { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct options, applying defaults. + * @param options optional override options. + */ + constructor(options = {}) { + this.options = _merge({}, Options.DEFAULTS, options); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Unwrap options. + * @returns {*} + */ + unwrap() { + return this.options; + } +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Default options, overridden by anything of the same name. + */ +Options.DEFAULTS = { + log: { + level: Logger.WARN, + }, + demo: false, + useHtmlSelect: true, + diagram: { + svg: { + x: 0, + y: 0, + width: 1600, + height: 1200, + margin: 50, + floodColor: '#009fdb', + scale: { + height: true, + width: true, + minimum: 0.25, + }, + }, + title: { + height: 0, + }, + metadata: false, + lifelines: { + header: { + height: 225, + width: 350, + wrapWords: 14, + wrapLines: 18, + maxLines: 5, + }, + occurrences: { + marginTop: 50, + marginBottom: 75, + foreshortening: 5, + width: 50, + }, + spacing: { + horizontal: 400, + vertical: 400, + }, + }, + messages: { + label: { + wrapWords: 14, + wrapLines: 18, + maxLines: 4, + }, + }, + fragments: { + leftMargin: 150, + topMargin: 200, + widthMargin: 300, + heightMargin: 350, + label: { + wrapWords: 50, + wrapLines: 50, + maxLines: 2, + }, + }, + }, + validation: { + lifeline: { + maxLength: 100, + defaultValue: '', + replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g, + }, + message: { + maxLength: 100, + defaultValue: '', + replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g, + }, + notes: { + maxLength: 255, + defaultValue: '', + }, + guard: { + maxLength: 80, + defaultValue: '', + replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g, + }, + }, +}; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx new file mode 100644 index 0000000000..20b06922c8 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx @@ -0,0 +1,268 @@ + +import React from 'react'; + +import Common from '../../common/Common'; +import Logger from '../../common/Logger'; +import Diagram from '../diagram/Diagram'; +import Dialog from '../dialog/Dialog'; +import Editor from '../editor/Editor'; +import Export from '../export/Export'; +import Overlay from '../overlay/Overlay'; + +/** + * Application controller, also a view. + */ +export default class Application extends React.Component { + + /** + * Construct application view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + + this.sequencer = Common.assertNotNull(props.sequencer); + this.model = this.sequencer.getModel(); + this.metamodel = this.sequencer.getMetamodel(); + this.options = props.options; + Logger.setLevel(this.options.unwrap().log.level); + + // Bindings. + + this.showInfoDialog = this.showInfoDialog.bind(this); + this.showEditDialog = this.showEditDialog.bind(this); + this.showConfirmDialog = this.showConfirmDialog.bind(this); + this.hideOverlay = this.hideOverlay.bind(this); + this.onMouseMove = this.onMouseMove.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get application options. + * @returns JSON options, see Options.js. + */ + getOptions() { + return this.options.unwrap(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set diagram name. + * @param n diagram (human-readable) name. + */ + setName(n) { + this.diagram.setName(n); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set diagram model. + * @param model diagram instance. + */ + setModel(model) { + + Common.assertNotNull(model); + + this.model = model; + + if (this.editor) { + this.editor.render(); + } + + if (this.diagram) { + this.diagram.render(); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get Model wrapper. + * @returns Model. + */ + getModel() { + return this.model; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get SVG element. + * @returns {*} + */ + getSVG() { + return this.diagram.getSVG(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get top-level widget. Provides the demo toolbar with access to the public API. + * @returns {*} + */ + getSequencer() { + return this.sequencer; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Present info dialog. + * @param msg info message. + */ + showInfoDialog(msg) { + this.dialog.showInfoDialog(msg); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Present error dialog. + * @param msg error message. + */ + showErrorDialog(msg) { + this.dialog.showErrorDialog(msg); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Present confirmation dialog. + * @param msg info message. + * @param cb callback function to be invoked on OK. + */ + showConfirmDialog(msg, cb) { + Common.assertType(cb, 'Function'); + this.dialog.showConfirmDialog(msg, cb); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Present edit (textarea) dialog. + * @param msg prompt. + * @param text current edit text. + * @param cb callback function to be invoked on OK, taking the updated text + * as an argument. + */ + showEditDialog(msg, text, cb) { + this.dialog.showEditDialog(msg, text, cb); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select lifeline by ID. + * @param id lifeline ID. + */ + selectLifeline(id) { + if (this.editor) { + this.editor.selectLifeline(id); + } + if (this.diagram) { + this.diagram.selectLifeline(id); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select message by ID. + * @param id message ID. + */ + selectMessage(id) { + if (this.editor) { + this.editor.selectMessage(id); + } + if (this.diagram) { + this.diagram.selectMessage(id); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * (Re)render just the diagram. + */ + renderDiagram() { + this.diagram.redraw(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show overlay between application and modal dialog. + */ + showOverlay() { + if (this.overlay) { + this.overlay.setVisible(true); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Hide overlay between application and modal dialog. + */ + hideOverlay() { + if (this.overlay) { + this.overlay.setVisible(false); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Capture mouse move events, for resize. + * @param event move event. + */ + onMouseMove(event) { + if (this.editor) { + this.editor.onMouseMove(event); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Propagate mouse event to the editor that manages the resize. + */ + onMouseUp() { + if (this.editor) { + this.editor.onMouseUp(); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render current model state. + */ + render() { + + return ( + +
+ + { this.editor = r; }} /> + { this.diagram = r; }} /> + { this.dialog = r; }} /> + + { this.overlay = r; }} /> + +
+ ); + } + +} + +/** React properties. */ +Application.propTypes = { + options: React.PropTypes.object.isRequired, + sequencer: React.PropTypes.object.isRequired, +}; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx new file mode 100644 index 0000000000..f2da7a5a1b --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx @@ -0,0 +1,896 @@ + +import React from 'react'; +import _template from 'lodash/template'; +import _merge from 'lodash/merge'; +import d3 from 'd3'; + +import Common from '../../common/Common'; +import Logger from '../../common/Logger'; +import Popup from './components/popup/Popup'; + +/** + * SVG diagram view. + */ +export default class Diagram extends React.Component { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct React view. + * @param props properties. + * @param context context. + */ + constructor(props, context) { + super(props, context); + + this.application = Common.assertNotNull(props.application); + this.options = this.application.getOptions().diagram; + + this.events = {}; + this.state = { + height: 0, + width: 0, + }; + + this.templates = { + diagram: _template(require('./templates/diagram.html')), + lifeline: _template(require('./templates/lifeline.html')), + message: _template(require('./templates/message.html')), + occurrence: _template(require('./templates/occurrence.html')), + fragment: _template(require('./templates/fragment.html')), + title: _template(require('./templates/title.html')), + }; + + this.handleResize = this.handleResize.bind(this); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set diagram name. + * @param n name. + */ + setName(n) { + this.svg.select('').text(n); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get SVG from diagram. + * @returns {*|string} + */ + getSVG() { + const svg = this.svg.node().outerHTML; + return svg.replace(' 0) ? `height:${titleHeight}` : 'asdcs-hidden'; + + return ( +
+
{name}
+
{ this.wrapper = r; }}>
+ { this.popup = r; }} /> +
+ ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + redraw() { + this.updateSVG(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Initial render. + */ + componentDidMount() { + window.addEventListener('resize', this.handleResize); + this.updateSVG(); + + // Insurance: + + setTimeout(() => { + this.handleResize(); + }, 500); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render on update. + */ + componentDidUpdate() { + this.updateSVG(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Redraw SVG diagram. So far it's fast enough that it doesn't seem to matter whether + * it's completely redrawn. + */ + updateSVG() { + + if (!this.svg) { + const svgparams = _merge({}, this.options.svg); + this.wrapper.innerHTML = this.templates.diagram(svgparams); + this.svg = d3.select(this.wrapper).select('svg'); + } + + if (this.state.height === 0) { + + // We'll get a resize event, and the height will be non-zero when it's actually time. + + return; + } + + if (this.state.height && this.state.width) { + const margin = this.options.svg.margin; + const x = -margin; + const y = -margin; + const height = this.state.height + (margin * 2); + const width = this.state.width + (margin * 2); + const viewBox = `${x} ${y} ${width} ${height}`; + this.svg.attr('viewBox', viewBox); + } + + + // If we've already rendered, then save the current scale/translate so that we + // can reapply it after rendering. + + const gContentSelection = this.svg.selectAll('g.asdcs-diagram-content'); + if (gContentSelection.size() === 1) { + const transform = gContentSelection.attr('transform'); + if (transform) { + this.savedTransform = transform; + } + } + + // Empty the document. We're starting again. + + this.svg.selectAll('.asdcs-diagram-content').remove(); + + // Extract the model. + + const model = this.application.getModel(); + if (!model) { + return; + } + const modelJSON = model.unwrap(); + + // Extract dimension options. + + const header = this.options.lifelines.header; + const spacing = this.options.lifelines.spacing; + + // Make separate container elements so that we can control Z order. + + const gContent = this.svg.append('g').attr('class', 'asdcs-diagram-content'); + const gLifelines = gContent.append('g').attr('class', 'asdcs-diagram-lifelines'); + const gCanvas = gContent.append('g').attr('class', 'asdcs-diagram-canvas'); + gCanvas.append('g').attr('class', 'asdcs-diagram-occurrences'); + gCanvas.append('g').attr('class', 'asdcs-diagram-fragments'); + gCanvas.append('g').attr('class', 'asdcs-diagram-messages'); + + // Lifelines ----------------------------------------------------------------------------------- + + const actorsById = {}; + const positionsByMessageId = {}; + const lifelines = []; + for (const actor of modelJSON.diagram.lifelines) { + const x = (header.width / 2) + (lifelines.length * spacing.horizontal); + Diagram._processLifeline(actor, x); + lifelines.push({ x, actor }); + actorsById[actor.id] = actor; + } + + // Messages ------------------------------------------------------------------------------------ + + // Analyze occurrence information. + + const occurrences = model.analyzeOccurrences(); + const fragments = model.analyzeFragments(); + let y = this.options.lifelines.header.height + spacing.vertical; + let messageIndex = 0; + for (const step of modelJSON.diagram.steps) { + if (step.message) { + positionsByMessageId[step.message.id] = positionsByMessageId[step.message.id] || {}; + positionsByMessageId[step.message.id].y = y; + this._drawMessage(gCanvas, step.message, y, actorsById, + positionsByMessageId, ++messageIndex, occurrences, fragments); + } + y += spacing.vertical; + } + + // --------------------------------------------------------------------------------------------- + + // Draw the actual (dashed) lifelines in a background . + + this._drawLifelines(gLifelines, lifelines, y); + + // Initialize mouse event handlers. + + this._initMouseEvents(gLifelines, gCanvas); + + // Scale to fit. + + const bb = gContent.node().getBBox(); + this._initZoom(gContent, bb.width, bb.height); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw message into SVG canvas. + * @param gCanvas container. + * @param message message to be rendered. + * @param y current y position. + * @param actorsById actor lookup. + * @param positionsByMessageId x- and y-position of each message. + * @param messageIndex where we are in the set of messages to be rendered. + * @param oData occurrences info. + * @param fData fragments info. + * @private + */ + _drawMessage(gCanvas, message, y, actorsById, positionsByMessageId, + messageIndex, oData, fData) { + + Common.assertNotNull(oData); + + const request = message.type === 'request'; + const fromActor = request ? actorsById[message.from] : actorsById[message.to]; + const toActor = request ? actorsById[message.to] : actorsById[message.from]; + + if (!fromActor) { + Logger.warn(`Cannot draw message ${JSON.stringify(message)}: 'from' not found.`); + return; + } + + if (!toActor) { + Logger.warn(`Cannot draw message ${JSON.stringify(message)}: 'to' not found.`); + return; + } + + // Occurrences. -------------------------------------------------------------------------------- + + if (message.occurrence) { + Logger.debug(`Found occurrence for ${message.name}: ${JSON.stringify(message.occurrence)}`); + } + const activeTo = Diagram._calcActive(oData, toActor.id); + this._drawOccurrence(gCanvas, oData, positionsByMessageId, fromActor, message.id); + this._drawOccurrence(gCanvas, oData, positionsByMessageId, toActor, message.id); + const activeFrom = Diagram._calcActive(oData, fromActor.id); + + // Messages. ----------------------------------------------------------------------------------- + + const gMessages = gCanvas.select('g.asdcs-diagram-messages'); + + // Save positions for later. + + const positions = positionsByMessageId[message.id]; + positions.x0 = fromActor.x; + positions.x1 = toActor.x; + + // Calculate. + + const leftToRight = fromActor.x < toActor.x; + const loopback = (message.to === message.from); + const x1 = this._calcMessageX(activeTo, toActor.x, true, leftToRight); + const x0 = loopback ? x1 : this._calcMessageX(activeFrom, fromActor.x, false, leftToRight); + + let messagePath; + if (loopback) { + + // To self. + + messagePath = `M${x1},${y}`; + messagePath = `${messagePath} L${x1 + 200},${y}`; + messagePath = `${messagePath} L${x1 + 200},${y + 50}`; + messagePath = `${messagePath} L${x1},${y + 50}`; + } else { + + // Between lifelines. + + messagePath = `M${x0},${y}`; + messagePath = `${messagePath} L${x1},${y}`; + } + + const styles = Diagram._getMessageStyles(message); + + // Split message over lines. + + const messageWithPrefix = `${messageIndex}. ${message.name}`; + const maxLines = this.options.messages.label.maxLines; + const wrapWords = this.options.messages.label.wrapWords; + const wrapLines = this.options.messages.label.wrapLines; + const messageLines = Common.tokenize(messageWithPrefix, wrapWords, wrapLines, maxLines); + + const messageTxt = this.templates.message({ + id: message.id, + classes: styles.css, + marker: styles.marker, + dasharray: styles.dasharray, + labels: messageLines, + lines: maxLines, + path: messagePath, + index: messageIndex, + x0, x1, y, + }); + + const messageEl = Common.txt2dom(messageTxt); + const gMessage = gMessages.append('g'); + Common.dom2svg(messageEl, gMessage); + + // Set the background's bounding box to that of the text, + // so that they fit snugly. + + const labelBB = gMessage.select('.asdcs-diagram-message-label').node().getBBox(); + gMessage.select('.asdcs-diagram-message-label-bg') + .attr('x', labelBB.x) + .attr('y', labelBB.y) + .attr('height', labelBB.height) + .attr('width', labelBB.width); + + // Fragments. ---------------------------------------------------------------------------------- + + const fragment = fData[message.id]; + if (fragment) { + + // It ends on this message. + + this._drawFragment(gCanvas, fragment, positionsByMessageId); + + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw a single occurrence. + * @param gCanvas container. + * @param oData occurrence data. + * @param positionsByMessageId map of y positions by message ID. + * @param actor wrapper containing lifeline ID (.id), position (.x) and name (.name). + * @param messageId message identifier. + * @private + */ + _drawOccurrence(gCanvas, oData, positionsByMessageId, actor, messageId) { + + Common.assertType(oData, 'Object'); + Common.assertType(positionsByMessageId, 'Object'); + Common.assertType(actor, 'Object'); + Common.assertType(messageId, 'String'); + + const gOccurrences = gCanvas.select('g.asdcs-diagram-occurrences'); + + const oOptions = this.options.lifelines.occurrences; + const oWidth = oOptions.width; + const oHalfWidth = oWidth / 2; + const oForeshortening = oOptions.foreshortening; + const oMarginTop = oOptions.marginTop; + const oMarginBottom = oOptions.marginBottom; + const o = oData[actor.id]; + + const active = Diagram._calcActive(oData, actor.id); + + const x = (actor.x - oHalfWidth) + (active * oWidth); + const positions = positionsByMessageId[messageId]; + const y = positions.y; + + let draw = true; + if (o) { + + if (o.start[messageId]) { + + // Starting, but drawing nothing until we find the end. + + o.active.push(messageId); + draw = false; + + } else if (active > 0) { + + const startMessageId = o.stop[messageId]; + if (startMessageId) { + + // OK, it ends here. Draw the occurrence box. + + o.active.pop(); + const foreshorteningY = active * oForeshortening; + const startY = positionsByMessageId[startMessageId].y; + const height = ((oMarginTop + oMarginBottom) + (y - startY)) - (foreshorteningY * 2); + const oProps = { + x: (actor.x - oHalfWidth) + ((active - 1) * oWidth), + y: ((startY - oMarginTop) + foreshorteningY), + height, + width: oWidth, + }; + + const occurrenceTxt = this.templates.occurrence(oProps); + const occurrenceEl = Common.txt2dom(occurrenceTxt); + Common.dom2svg(occurrenceEl, gOccurrences.append('g')); + + } + draw = false; + } + } + + if (draw) { + + // Seems this is a singleton occurrence. We just draw a wee box around it. + + const height = (oMarginTop + oMarginBottom); + const occurrenceProperties = { x, y: y - oMarginTop, height, width: oWidth }; + const defaultTxt = this.templates.occurrence(occurrenceProperties); + const defaultEl = Common.txt2dom(defaultTxt); + Common.dom2svg(defaultEl, gOccurrences.append('g')); + } + + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw box(es) around fragment(s). + * @param gCanvas container. + * @param fragment fragment definition, corresponding to its final (stop) message. + * @param positionsByMessageId message dimensions. + * @private + */ + _drawFragment(gCanvas, fragment, positionsByMessageId) { + + const optFragments = this.options.fragments; + const gFragments = gCanvas.select('g.asdcs-diagram-fragments'); + const p1 = positionsByMessageId[fragment.stop]; + if (p1 && fragment.start && fragment.start.length > 0) { + + for (const start of fragment.start) { + + const message = this.application.getModel().getMessageById(start); + const bounds = this._calcFragmentBounds(message, fragment, positionsByMessageId); + if (bounds) { + + const maxLines = this.options.fragments.label.maxLines; + const wrapWords = this.options.fragments.label.wrapWords; + const wrapLines = this.options.fragments.label.wrapLines; + const lines = Common.tokenize(message.fragment.guard, wrapWords, wrapLines, maxLines); + + const params = { + id: start, + x: bounds.x0 - optFragments.leftMargin, + y: bounds.y0 - optFragments.topMargin, + height: (bounds.y1 - bounds.y0) + optFragments.heightMargin, + width: (bounds.x1 - bounds.x0) + optFragments.widthMargin, + operator: (message.fragment.operator || 'alt'), + lines, + }; + + const fragmentTxt = this.templates.fragment(params); + const fragmentEl = Common.txt2dom(fragmentTxt); + const gFragment = gFragments.append('g'); + Common.dom2svg(fragmentEl, gFragment); + + const labelBB = gFragment.select('.asdcs-diagram-fragment-guard').node().getBBox(); + gFragment.select('.asdcs-diagram-fragment-guard-bg') + .attr('x', labelBB.x) + .attr('y', labelBB.y) + .attr('height', labelBB.height) + .attr('width', labelBB.width); + + } else { + Logger.warn(`Bad fragment: ${JSON.stringify(fragment)}`); + } + } + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + _calcFragmentBounds(startMessage, fragment, positionsByMessageId) { + if (startMessage) { + const steps = this.application.getModel().unwrap().diagram.steps; + const bounds = { x0: 99999, x1: 0, y0: 99999, y1: 0 }; + let foundStart = false; + let foundStop = false; + for (const step of steps) { + const message = step.message; + if (message) { + if (message.id === startMessage.id) { + foundStart = true; + } + if (foundStart && !foundStop) { + const positions = positionsByMessageId[message.id]; + if (positions) { + bounds.x0 = Math.min(bounds.x0, Math.min(positions.x0, positions.x1)); + bounds.y0 = Math.min(bounds.y0, positions.y); + bounds.x1 = Math.max(bounds.x1, Math.max(positions.x0, positions.x1)); + bounds.y1 = Math.max(bounds.y1, positions.y); + } else { + // This probably means it hasn't been recorded yet, which is fine, because + // we draw fragments from where they END. + foundStop = true; + } + } + + if (message.id === fragment.stop) { + foundStop = true; + } + } + } + return bounds; + } + return undefined; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw all lifelines. + * @param gLifelines lifelines container. + * @param lifelines lifelines definitions. + * @param y height. + * @private + */ + _drawLifelines(gLifelines, lifelines, y) { + + const maxLines = this.options.lifelines.header.maxLines; + const wrapWords = this.options.lifelines.header.wrapWords; + const wrapLines = this.options.lifelines.header.wrapLines; + + for (const lifeline of lifelines) { + const lines = Common.tokenize(lifeline.actor.name, wrapWords, wrapLines, maxLines); + const lifelineTxt = this.templates.lifeline({ + x: lifeline.x, + y0: 0, + y1: y, + lines, + rows: maxLines, + headerHeight: this.options.lifelines.header.height, + headerWidth: this.options.lifelines.header.width, + id: lifeline.actor.id, + }); + + const lifelineEl = Common.txt2dom(lifelineTxt); + Common.dom2svg(lifelineEl, gLifelines.append('g')); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Initialize all mouse events. + * @param gLifelines lifelines container. + * @param gCanvas top-level canvas container. + * @private + */ + _initMouseEvents(gLifelines, gCanvas) { + + const self = this; + const source = 'asdcs'; + const origin = `${window.location.protocol}//${window.location.host}`; + + let timer; + gLifelines.selectAll('.asdcs-diagram-lifeline-selectable') + .on('mouseenter', function f() { + timer = setTimeout(() => { + self.application.selectLifeline(d3.select(this.parentNode).attr('data-id')); + }, 150); + }) + .on('mouseleave', () => { + clearTimeout(timer); + self.application.selectLifeline(); + }) + .on('click', function f() { + const id = d3.select(this.parentNode).attr('data-id'); + window.postMessage({ source, id, type: 'lifeline' }, origin); + }); + + gLifelines.selectAll('.asdcs-diagram-lifeline-heading-box') + .on('mouseenter', function f() { + timer = setTimeout(() => { + self.application.selectLifeline(d3.select(this.parentNode).attr('data-id')); + }, 150); + }) + .on('mouseleave', () => { + clearTimeout(timer); + self.application.selectLifeline(); + }) + .on('click', function f() { + const id = d3.select(this.parentNode).attr('data-id'); + window.postMessage({ source, id, type: 'lifelineHeader' }, origin); + }); + + gCanvas.selectAll('.asdcs-diagram-message-selectable') + .on('mouseenter', function f() { + self.events.message = { x: d3.event.pageX, y: d3.event.pageY }; + timer = setTimeout(() => { + self.application.selectMessage(d3.select(this.parentNode).attr('data-id')); + }, 200); + }) + .on('mouseleave', () => { + delete self.events.message; + clearTimeout(timer); + self.application.selectMessage(); + }) + .on('click', function f() { + const id = d3.select(this.parentNode).attr('data-id'); + window.postMessage({ source, id, type: 'message' }, origin); + }); + + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get CSS classes to be applied to a message, according to whether request/response + * or synchronous/asynchronous. + * @param message message being rendered. + * @returns CSS class name(s). + * @private + */ + static _getMessageStyles(message) { + + let marker = 'asdcsDiagramArrowSolid'; + let dasharray = ''; + let css = 'asdcs-diagram-message'; + if (message.type === 'request') { + css = `${css} asdcs-diagram-message-request`; + } else { + css = `${css} asdcs-diagram-message-response`; + marker = 'asdcsDiagramArrowOpen'; + dasharray = '30, 10'; + } + + if (message.asynchronous) { + css = `${css} asdcs-diagram-message-asynchronous`; + marker = 'asdcsDiagramArrowOpen'; + } else { + css = `${css} asdcs-diagram-message-synchronous`; + } + + return { css, marker, dasharray }; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Initialize or reinitialize zoom. This sets the initial zoom in the case of + * a re-rendering, and initializes the eventhandling in all cases. + * + * It does some fairly risky parsing of the 'transform' attribute, assuming that it + * can contain scale() and translate(). But only the zoom handler and us are writing + * the transform values, so that's probably OK. + * + * @param gContent container. + * @param width diagram width. + * @param height diagram height. + * @private + */ + _initZoom(gContent, width, height) { + + const zoomed = function zoomed() { + gContent.attr('transform', + `translate(${d3.event.translate})scale(${d3.event.scale})`); + }; + + const viewWidth = this.state.width || this.options.svg.width; + const viewHeight = this.state.height || this.options.svg.height; + const scaleMinimum = this.options.svg.scale.minimum; + const scaleWidth = viewWidth / width; + const scaleHeight = viewHeight / height; + + let scale = scaleMinimum; + if (this.options.svg.scale.width) { + scale = Math.max(scale, scaleWidth); + } + if (this.options.svg.scale.height) { + scale = Math.min(scale, scaleHeight); + } + + scale = Math.max(scale, scaleMinimum); + + let translate = [0, 0]; + if (this.savedTransform) { + const s = this.savedTransform; + const scaleStart = s.indexOf('scale('); + if (scaleStart !== -1) { + scale = parseFloat(s.substring(scaleStart + 6, s.length - 1)); + } + const translateStart = s.indexOf('translate('); + if (translateStart !== -1) { + const spec = s.substring(translateStart + 10, s.indexOf(')', translateStart)); + const tokens = spec.split(','); + translate = [parseFloat(tokens[0]), parseFloat(tokens[1])]; + } + + gContent.attr('transform', this.savedTransform); + } else { + gContent.attr('transform', `scale(${scale})`); + } + + const zoom = d3.behavior.zoom() + .scale(scale) + .scaleExtent([scaleMinimum, 10]) + .translate(translate) + .on('zoom', zoomed); + this.svg.call(zoom); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Hide from the linter the fact that we're modifying the lifeline. + * @param lifeline to be updated with X position. + * @param x X position. + * @private + */ + static _processLifeline(lifeline, x) { + const actor = lifeline; + actor.id = actor.id || actor.name; + actor.x = x; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Derive active occurrences for lifeline. + * @param oData occurrences data. + * @param lifelineId lifeline to be analyzed. + * @returns {number} + * @private + */ + static _calcActive(oData, lifelineId) { + const o = oData[lifelineId]; + let active = 0; + if (o && o.active) { + active = o.active.length; + } + return active; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Derive the X position of an occurrence on a lifeline, taking into account how + * many occurrences are active. + * @param active active count. + * @param x lifeline X position; basis for offset. + * @param arrow whether this is the arrow (to) end. + * @param leftToRight whether this message goes left-to-right. + * @returns {*} calculated X position for occurrence left-hand side. + * @private + */ + _calcMessageX(active, x, arrow, leftToRight) { + const width = this.options.lifelines.occurrences.width; + const halfWidth = width / 2; + const active0 = Math.max(0, active - 1); + let calculated = x + (active0 * width); + if (arrow) { + // End (ARROW). + if (leftToRight) { + calculated -= halfWidth; + } else { + calculated += halfWidth; + } + } else { + // Start (NOT ARROW). + if (leftToRight) { + calculated += halfWidth; + } else { + calculated -= halfWidth; + } + } + + return calculated; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show popup upon hovering over a messages that has associated notes. + * @param id + * @private + */ + _showNotesPopup(id) { + if (this.popup) { + if (id) { + const message = this.application.getModel().getMessageById(id); + if (message && message.notes && message.notes.length > 0 && this.events.message) { + this.popup.setState({ + visible: true, + left: this.events.message.x - 50, + top: this.events.message.y + 20, + notes: message.notes[0], + }); + } + } else { + this.popup.setState({ visible: false, notes: '' }); + } + } + } +} + + +Diagram.propTypes = { + application: React.PropTypes.object.isRequired, +}; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx new file mode 100644 index 0000000000..08c6da1e76 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx @@ -0,0 +1,94 @@ + + +import React from 'react'; + +import Icon from '../../../icons/Icon'; +import iconEdit from '../../../../../../../../res/ecomp/asdc/sequencer/sprites/icon/edit.svg'; + +/** + * A hover-over popup. It shows notes, but perhaps will be put to other uses. + * @param props React properties. + * @returns {XML} + * @constructor + */ +export default class Popup extends React.Component { + + // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct react view. + * @param props element properties (of which there are none). + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.state = { + top: 0, + left: 0, + visible: false, + notes: '', + }; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render view. + * @returns {XML} + */ + render() { + + // Build CSS + styles to position and configure popup. + + let top = this.state.top; + let left = this.state.left; + + const popupHeight = 200; + const popupWidth = 320; + + let auxCssVertical = 'top'; + let auxCssHorizontal = 'left'; + + if (this.state.top > (window.innerHeight - popupHeight)) { + top -= (popupHeight + 50); + auxCssVertical = 'bottom'; + } + + if (this.state.left > (window.innerWidth - popupWidth)) { + left -= (popupWidth - 80); + auxCssHorizontal = 'right'; + } + + const auxCss = `asdcs-diagram-popup-${auxCssVertical}${auxCssHorizontal}`; + const styles = { + top, + left, + display: (this.state.visible ? 'block' : 'none'), + }; + + // Render element. + + let notes = this.state.notes || ''; + if (notes.length > 255) { + notes = notes.substring(0, 255); + notes = `${notes} ...`; + } + + return ( +
+
Notes
+
+
+ +
+
+
+ {notes} +
+
+
+
+
+ ); + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/diagram.html b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/diagram.html new file mode 100644 index 0000000000..22893ce864 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/diagram.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/fragment.html b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/fragment.html new file mode 100644 index 0000000000..812f5fcfb8 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/fragment.html @@ -0,0 +1,18 @@ + + + + + + + <%-operator%> + + + + <% + for (var lineIndex = 0; lineIndex < lines.length ; lineIndex++) { + %><%- lines[lineIndex] %><% + }%> + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/lifeline.html b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/lifeline.html new file mode 100644 index 0000000000..cd01d42c5a --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/lifeline.html @@ -0,0 +1,19 @@ + + + + + <% + for (var linesIndex = 0; linesIndex < lines.length && linesIndex < rows ; linesIndex++) { + %><%- lines[linesIndex] %><% + } + %> + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/message.html b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/message.html new file mode 100644 index 0000000000..bd4c33a016 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/message.html @@ -0,0 +1,29 @@ + + + <% + var delta = 40; + var x = (x0 + x1) / 2; + var y0 = y - ((labels.length + 1) * delta); + %> + + + + <% + for (var labelIndex = 0; labelIndex < labels.length && labelIndex < lines ; labelIndex++) { + %><%- labels[labelIndex] %><% + }%> + + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/occurrence.html b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/occurrence.html new file mode 100644 index 0000000000..0af9ff3d68 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/occurrence.html @@ -0,0 +1,7 @@ + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/title.html b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/title.html new file mode 100644 index 0000000000..b7a5d68a6d --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/templates/title.html @@ -0,0 +1,3 @@ + + <%-title%> + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx new file mode 100644 index 0000000000..4429d80bc6 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx @@ -0,0 +1,222 @@ + + +import React from 'react'; + +import Icon from '../icons/Icon'; +import iconQuestion from '../../../../../../res/ecomp/asdc/sequencer/sprites/icon/question.svg'; +import iconExclaim from '../../../../../../res/ecomp/asdc/sequencer/sprites/icon/exclaim.svg'; +import iconInfo from '../../../../../../res/ecomp/asdc/sequencer/sprites/icon/info.svg'; +import iconEdit from '../../../../../../res/ecomp/asdc/sequencer/sprites/icon/edit.svg'; +import iconClose from '../../../../../../res/ecomp/asdc/sequencer/sprites/icon/close.svg'; + +/** + * Multi-purpose dialog. Rendered into the page on initialization, and then + * configured, shown and hidden as required. + */ +export default class Dialog extends React.Component { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + */ + constructor(props, context) { + + super(props, context); + + this.MODE = { + INFO: { + icon: 'asdcs-icon-info', + heading: 'Information', + }, + ERROR: { + icon: 'asdcs-icon-exclaim', + heading: 'Error', + }, + EDIT: { + icon: 'asdcs-icon-edit', + heading: 'Edit', + edit: true, + confirm: true, + }, + CONFIRM: { + icon: 'asdcs-icon-question', + heading: 'Confirm', + confirm: true, + }, + }; + + this.state = { + mode: this.MODE.INFO, + message: '', + text: '', + visible: false, + }; + + // Bindings. + + this.onClickOK = this.onClickOK.bind(this); + this.onClickCancel = this.onClickCancel.bind(this); + this.onChangeText = this.onChangeText.bind(this); + this.showConfirmDialog = this.showConfirmDialog.bind(this); + this.showInfoDialog = this.showInfoDialog.bind(this); + this.showEditDialog = this.showEditDialog.bind(this); + this.showErrorDialog = this.showErrorDialog.bind(this); + this.showDialog = this.showDialog.bind(this); + + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show info dialog. + * @param message info message. + */ + showInfoDialog(message) { + this.showDialog(this.MODE.INFO, { message }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show error dialog. + * @param message error message. + */ + showErrorDialog(message) { + this.showDialog(this.MODE.ERROR, { message }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show edit dialog. + * @param message dialog message. + * @param text current edit text. + * @param callback callback function to be invoked on OK. + */ + showEditDialog(message, text, callback) { + this.showDialog(this.MODE.EDIT, { message, text, callback }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show confirmation dialog. + * @param message dialog message. + * @param callback callback function to be invoked on OK. + */ + showConfirmDialog(message, callback) { + this.showDialog(this.MODE.CONFIRM, { message, callback }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle buttonclick. + */ + onClickOK() { + this.props.application.hideOverlay(); + this.setState({ visible: false }); + if (this.callback) { + + // So far the only thing we can return is edit text, but send it back + // as properties to allow for future return values. + + this.callback({ text: this.state.text }); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle buttonclick. + */ + onClickCancel() { + this.props.application.hideOverlay(); + this.setState({ visible: false }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle text changes. + * @param event update event. + */ + onChangeText(event) { + this.setState({ text: event.target.value }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show dialog in specified configuration. + * @param mode dialog mode. + * @param args dialog parameters, varying slightly by dialog type. + * @private + */ + showDialog(mode, args) { + this.props.application.showOverlay(); + this.callback = args.callback; + this.setState({ + mode, + visible: true, + message: args.message || '', + text: args.text || '', + }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render dialog into the page, initially hidden. + */ + render() { + + const dialogClass = (this.state.visible) ? '' : 'asdcs-hidden'; + const cancelClass = (this.callback) ? '' : 'asdcs-hidden'; + const textClass = (this.state.mode === this.MODE.EDIT) ? '' : 'asdcs-hidden'; + + return ( +
+
{this.state.mode.heading}
+
+ +
+
+ + + + +
+
+ {this.state.message} +
+
+ +
+ ); + } +} + +Source.propTypes = { + application: React.PropTypes.object.isRequired, +}; + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx new file mode 100644 index 0000000000..dd75180b2a --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx @@ -0,0 +1,275 @@ +/*! + * 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 Common from '../../../../common/Common'; + +import iconPlus from '../../../../../../../../res/ecomp/asdc/sequencer/sprites/icon/plus.svg'; +import iconOpen from '../../../../../../../../res/ecomp/asdc/sequencer/sprites/icon/open.svg'; + +/** + * Toolbar view. Buttons offered in the toolbar depend on the mode. Unless in demo mode, + * all you get are the buttons for toggling between JSON/YAML/Designer. + */ +export default class Toolbar extends React.Component { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + */ + constructor(props, context) { + super(props, context); + this.application = Common.assertType(this.props.application, 'Object'); + this.editor = Common.assertType(this.props.editor, 'Object'); + this.mode = 'design'; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set editor mode, one of {design, json, yaml}. + * @param mode + */ + setMode(mode = 'design') { + this.mode = mode; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render into the DOM. + */ + render() { + + const demo = this.application.getOptions().demo; + const demoCss = demo ? '' : 'asdc-hide'; + + return ( +
+
+ + + + + + +
+
+ + + +
+
+ ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Initialize eventhandlers. + * @private + * + _initEvents() { + + $('button.asdcs-button-open', this.$el).click(() => { + this._doDemoOpen(); + }); + + $('button.asdcs-button-new', this.$el).click(() => { + this._doDemoNew(); + }); + + $('button.asdcs-button-save', this.$el).click(() => { + this._doDemoSave(); + }); + + $('button.asdcs-button-upload', this.$el).click(() => { + this._doDemoUpload(); + }); + + $('button.asdcs-button-download', this.$el).click(() => { + this._doDemoDownload(); + }); + + $('button.asdcs-button-validate', this.$el).click(() => { + this._doDemoValidate(); + }); + + $('button.asdcs-button-json', this.$el).click((e) => { + if ($(e.target).hasClass('asdcs-active')) { + return; + } + this.editor.toggleToJSON(); + }); + + $('button.asdcs-button-yaml', this.$el).click((e) => { + if ($(e.target).hasClass('asdcs-active')) { + return; + } + this.editor.toggleToYAML(); + }); + + $('button.asdcs-button-design', this.$el).click((e) => { + if ($(e.target).hasClass('asdcs-active')) { + return; + } + this.editor.toggleToDesign(); + }); + } + */ + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Demo action. + * + _doDemoOpen() { + const complete = function complete() { + const sequencer = this.application.getSequencer(); + const scenarios = sequencer.getDemoScenarios(); + sequencer.setModel(scenarios.getECOMP()); + }; + this.application.showConfirmDialog('[DEMO MODE] Open a canned DEMO sequence ' + + 'via the public #setModel() API?', complete); + + } + */ + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Demo action. + * + _doDemoNew() { + const complete = function complete() { + const sequencer = this.application.getSequencer(); + sequencer.newModel(); + }; + this.application.showConfirmDialog('[DEMO MODE] Create an empty sequence via the ' + + 'public #newModel() API?', complete); + } + */ + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Demo action. + * + _doDemoSave() { + const sequencer = this.application.getSequencer(); + Logger.info(`[DEMO MODE] model:\n${JSON.stringify(sequencer.getModel(), null, 4)}`); + this.application.showInfoDialog('[DEMO MODE] Retrieved model via the public #getModel ' + + 'API and logged its JSON to the console.'); + } + */ + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Demo action. + * + _doDemoUpload() { + const sequencer = this.application.getSequencer(); + const svg = sequencer.getSVG(); + // console.log(`[DEMO MODE] SVG:\n${svg}`); + const $control = this.$el.closest('.asdcs-control'); + Logger.info(`parent: ${$control.length}`); + const $form = $('form.asdcs-export', $control); + Logger.info(`form: ${$form.length}`); + $('input[name=svg]', $form).val(svg); + try { + $form.submit(); + } catch (e) { + Logger.error(e); + this.application.showErrorDialog('[DEMO MODE] Export service not available. Retrieved ' + + 'SVG via the public #getSVG API and dumped it to the console.'); + } + } + */ + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Demo action. + * + _doDemoDownload() { + const json = JSON.stringify(this.application.getSequencer().getModel()); + const $control = this.$el.closest('.asdcs-control'); + const $a = $('').appendTo($control); + $a.attr('href', `data:application/json;charset=utf-8,${encodeURIComponent(json)}`); + $a[0].click(); + $a.remove(); + } + */ + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Demo action. + * + _doDemoValidate() { + this.application.showInfoDialog('[DEMO MODE] Dumping validation result to the console.'); + const errors = this.application.getModel().validate(); + Logger.info(`[DEMO MODE] Validation: ${JSON.stringify(errors, null, 4)}`); + } + */ +} + +Toolbar.propTypes = { + application: React.PropTypes.object.isRequired, + editor: React.PropTypes.object.isRequired, +}; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx new file mode 100644 index 0000000000..529ae92ded --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx @@ -0,0 +1,31 @@ +/*! + * 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'; + +const Export = function Export() { + return ( +
+ + + + + +
+ ); +}; + +export default Export; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx new file mode 100644 index 0000000000..6bc04f997f --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx @@ -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 React from 'react'; + +/** + * Simple icon view. + * @param glyph glyph definition, from import. + * @param className optional classname, for svg element. + * @returns {XML} + * @constructor + */ +const Icon = function Icon({ glyph, className }) { + return ( + + + + ); +}; + +/** Declare properties. */ +Icon.propTypes = { + className: React.PropTypes.string, + glyph: React.PropTypes.string.isRequired, +}; + +export default Icon; + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx new file mode 100644 index 0000000000..817f4f1697 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx @@ -0,0 +1,61 @@ +/*! + * 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'; + +/** + * Overlay view. + */ +export default class Overlay extends React.Component { + + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.state = { + visible: false, + }; + this.setVisible = this.setVisible.bind(this); + } + + /** + * Set visibility. + * @param visible true if visible. + */ + setVisible(visible) { + this.setState({ + visible, + }); + } + + /** + * Render view. + * @returns {XML} + */ + render() { + const display = this.state.visible ? 'block' : 'none'; + return ( +
+
+ ); + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js new file mode 100644 index 0000000000..82e8ada588 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js @@ -0,0 +1,94 @@ +/*! + * 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 _merge from 'lodash/merge'; + +import Common from '../common/Common'; + +/** + * Rules governing what a definition can contain. + */ +export default class Metamodel { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct from JSON definition. + * @param json schema definition. + */ + constructor(json) { + Common.assertType(json, 'Object'); + const dfault = require('./templates/default.metamodel.json'); + this.json = _merge({}, dfault, json); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get schema identifier. + * @returns ID. + */ + getId() { + return this.json.diagram.metadata.id; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get lifeline constraints. + * @returns {*} + */ + getConstraints() { + return this.json.diagram.lifelines.constraints; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get lifeline metadata by lifeline ID. + * @param id sought lifeline. + * @returns lifeline if found. + */ + getLifelineById(id) { + for (const lifeline of this.json.diagram.lifelines.lifelines) { + if (lifeline.id === id) { + return lifeline; + } + } + return undefined; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get original JSON. + * @returns JSON. + */ + unwrap() { + return this.json; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get default schema. + * @returns Metamodel default (permissive) Metamodel. + */ + static getDefault() { + return new Metamodel({}); + } + +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js new file mode 100644 index 0000000000..4ecfc0b5f7 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js @@ -0,0 +1,87 @@ +/*! + * 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 Common from '../common/Common'; +import Metamodel from './Metamodel'; + +/** + * A simple lookup for schemas by ID. + */ +export default class Metamodels { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct metamodels from provided JSON definitions. + * @param metamodels JSON metamodel definitions. + */ + constructor(metamodels) { + + Common.assertType(metamodels, 'Array'); + + this.lookup = {}; + + // Save each metamodel. It's up to the Metamodel class to make sense of + // potentially nonsense metamodel definitions. + + for (const json of metamodels) { + const metamodel = new Metamodel(json); + this.lookup[metamodel.getId()] = metamodel; + } + + // Set (or override) the default metamodel with the inlined one. + + this.lookup.$ = Metamodel.getDefault(); + Common.assertInstanceOf(this.lookup.$, Metamodel); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get Metamodel by its @id. + * @param id identifier. + * @returns Metamodel, or undefined if no matching metamodel found. + */ + getMetamodel(id) { + return this.lookup[id]; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get the default (permissive) metamodel. + * @returns default Metamodel. + */ + getDefault() { + return this.lookup.$; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get metamodel by its @id, falling back to the default. + * @param id identifier. + * @returns matching metamodel, or default. + */ + getMetamodelOrDefault(id) { + const metamodel = this.getMetamodel(id); + if (metamodel) { + return metamodel; + } + return this.getDefault(); + } + +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js new file mode 100644 index 0000000000..1e68cd6034 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js @@ -0,0 +1,512 @@ +/*! + * 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 _merge from 'lodash/merge'; +// import jsonschema from 'jsonschema'; + +import Common from '../common/Common'; +import Metamodel from './Metamodel'; + +/** + * A wrapper for a model instance. + */ +export default class Model { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct model from model JSON. JSON is assumed to be in more or less + * the correct structure, but it's OK if it's missing IDs. + * + * @param json initial JSON; will be updated in situ. + * @param metamodel Metaobject definition. + */ + constructor(json, metamodel) { + + if (metamodel) { + Common.assertInstanceOf(metamodel, Metamodel); + } + + this.metamodel = metamodel || Metamodel.getDefault(); + Common.assertInstanceOf(this.metamodel, Metamodel); + + this.jsonschema = require('./schema/asdc_sequencer_schema.json'); + this.templates = { + defaultModel: require('./templates/default.model.json'), + defaultMetamodel: require('./templates/default.metamodel.json'), + }; + + this.model = this._preprocess(Common.assertType(json, 'Object')); + Common.assertPlainObject(this.model); + + this.renumber(); + + this.addLifeline = this.addLifeline.bind(this); + this.addMessage = this.addMessage.bind(this); + this.renumber = this.renumber.bind(this); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Unwrap to get model object. + * @returns {*} + */ + unwrap() { + return Common.assertPlainObject(this.model); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get the metamodel which defines valid states for this model. + * @returns Metamodel definition. + */ + getMetamodel() { + return Common.assertInstanceOf(this.metamodel, Metamodel); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Find lifeline by its ID. + * @param id lifeline ID. + * @returns lifeline object, if found. + */ + getLifelineById(id) { + for (const lifeline of this.model.diagram.lifelines) { + if (lifeline.id === id) { + return lifeline; + } + } + return undefined; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get message by ID. + * @param id message ID. + * @returns message if matched. + */ + getMessageById(id) { + Common.assertNotNull(id); + const step = this.getStepByMessageId(id); + if (step) { + return step.message; + } + return undefined; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get step by message ID. + * @param id step ID. + * @returns step if matched. + */ + getStepByMessageId(id) { + Common.assertNotNull(id); + for (const step of this.model.diagram.steps) { + if (step.message && step.message.id === id) { + return step; + } + } + return undefined; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add message to steps. + * @returns {{}} + */ + addMessage(index) { + const d = this.model.diagram; + const step = {}; + step.message = {}; + step.message.id = Model._guid(); + step.message.name = '[Unnamed Message]'; + step.message.type = 'request'; + step.message.from = d.lifelines.length > 0 ? d.lifelines[0].id : -1; + step.message.to = d.lifelines.length > 1 ? d.lifelines[1].id : -1; + if (index >= 0) { + d.steps.splice(index, 0, step); + } else { + d.steps.push(step); + } + this.renumber(); + return step; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete message with ID. + * @param id to be deleted. + */ + deleteMessageById(id) { + Common.assertNotNull(id); + const step = this.getStepByMessageId(id); + if (step) { + const index = this.model.diagram.steps.indexOf(step); + if (index !== -1) { + this.model.diagram.steps.splice(index, 1); + } + } + this.renumber(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add lifeline to lifelines. + * @param index optional index. + * @returns {{}} + */ + addLifeline(index) { + const lifeline = {}; + lifeline.id = Model._guid(); + lifeline.name = '[Unnamed Lifeline]'; + if (index >= 0) { + this.model.diagram.lifelines.splice(index, 0, lifeline); + } else { + this.model.diagram.lifelines.push(lifeline); + } + this.renumber(); + return lifeline; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete lifeline with ID. + * @param id to be deleted. + */ + deleteLifelineById(id) { + Common.assertNotNull(id); + this.deleteStepsByLifelineId(id); + const lifeline = this.getLifelineById(id); + if (lifeline) { + const index = this.model.diagram.lifelines.indexOf(lifeline); + if (index !== -1) { + this.model.diagram.lifelines.splice(index, 1); + } + } + this.renumber(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete all steps corresponding to lifeline. + * @param id lifeline ID. + */ + deleteStepsByLifelineId(id) { + Common.assertNotNull(id); + const steps = this.getStepsByLifelineId(id); + for (const step of steps) { + this.deleteMessageById(step.message.id); + } + this.renumber(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get all steps corresponding to lifeline. + * @param id lifeline ID. + * @return steps from/to lifeline. + */ + getStepsByLifelineId(id) { + Common.assertNotNull(id); + const steps = []; + for (const step of this.model.diagram.steps) { + if (step.message) { + if (step.message.from === id || step.message.to === id) { + steps.push(step); + } + } + } + return steps; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Validate model. Disabled, because we removed the jsonschema dependency. + * @returns {Array} of validation errors, if any. + */ + validate() { + const errors = []; + return errors; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Reorder messages. + * @param index message index. + * @param afterIndex new (after) index. + */ + reorderMessages(index, afterIndex) { + Common.assertType(index, 'Number'); + Common.assertType(afterIndex, 'Number'); + const steps = this.model.diagram.steps; + const element = steps[index]; + steps.splice(index, 1); + steps.splice(afterIndex, 0, element); + this.renumber(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Reorder lifelines. + * @param index lifeline index. + * @param afterIndex new (after) index. + */ + reorderLifelines(index, afterIndex) { + Common.assertType(index, 'Number'); + Common.assertType(afterIndex, 'Number'); + const lifelines = this.model.diagram.lifelines; + const element = lifelines[index]; + lifelines.splice(index, 1); + lifelines.splice(afterIndex, 0, element); + this.renumber(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Renumber lifelines and messages. + */ + renumber() { + const modelJSON = this.unwrap(); + let stepIndex = 1; + let lifelineIndex = 1; + for (const step of modelJSON.diagram.steps) { + if (step.message) { + step.message.index = stepIndex++; + } + } + for (const lifeline of modelJSON.diagram.lifelines) { + lifeline.index = lifelineIndex++; + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Build a simple, navigable dataset describing fragments. + * @returns {{}}, indexed by (stop) message ID, describing fragments. + */ + analyzeFragments() { + + const fData = {}; + + let depth = 0; + const modelJSON = this.unwrap(); + const open = []; + + const getData = function g(stop, fragment) { + let data = fData[stop]; + if (!data) { + data = { stop, start: [], fragment }; + fData[stop] = data; + } + return data; + }; + + const fragmentsByStart = {}; + for (const step of modelJSON.diagram.steps) { + if (step.message && step.message.fragment) { + const message = step.message; + const fragment = message.fragment; + if (fragment.start) { + fragmentsByStart[fragment.start] = fragment; + open.push(message.id); + depth++; + } + if (fragment.stop) { + if (open.length > 0) { + getData(message.id).start.push(open.pop()); + } + depth = Math.max(depth - 1, 0); + } + } + } + + if (open.length > 0) { + for (const o of open) { + getData(o, fragmentsByStart[o]).start.push(o); + } + } + + return fData; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Build a simple, navigable dataset describing occurrences. + * @returns a map, indexed by lifeline ID, of objects containing {start:[],stop:[],active[]}. + * @private + */ + analyzeOccurrences() { + + const oData = {}; + + // A few inline functions. They make this method kinda lengthy, but they + // reduce clutter in the class and keep it coherent, so it's OK. + + const getDataByLifelineId = function get(lifelineId) { + if (!oData[lifelineId]) { + oData[lifelineId] = { active: [], start: {}, stop: {} }; + } + return oData[lifelineId]; + }; + + const contains = function contains(array, value) { + return (array && (array.indexOf(value) !== -1)); + }; + + const process = function process(message, lifelineId) { + const oRule = message.occurrences; + if (oRule) { + + const oDataLifeline = getDataByLifelineId(lifelineId); + if (oDataLifeline) { + + // Record all starts. + + if (contains(oRule.start, lifelineId)) { + oDataLifeline.active.push(message.id); + oDataLifeline.start[message.id] = undefined; + } + + // Reconcile with stops. + + if (contains(oRule.stop, lifelineId)) { + const startMessageId = oDataLifeline.active.pop(); + oDataLifeline.stop[message.id] = startMessageId; + if (startMessageId) { + oDataLifeline.start[startMessageId] = message.id; + } + } + } + } + }; + + // Analyze start and end. + + const modelJSON = this.unwrap(); + for (const step of modelJSON.diagram.steps) { + if (step.message) { + const message = step.message; + if (message.occurrences) { + process(message, message.from); + process(message, message.to); + } + } + } + + // Reset active. (We used it, but it's not actually for us; it's for keeping + // track of active occurrences when rendering the diagram.) + + for (const lifelineId of Object.keys(oData)) { + oData[lifelineId].active = []; + } + + // Reconcile the start and end (message ID) maps for each lifeline, + // finding a "stop" for every start. Default to starting and stopping + // on the same message, which is the same as no occurrence. + + for (const lifelineId of Object.keys(oData)) { + const lifelineData = oData[lifelineId]; + for (const startId of Object.keys(lifelineData.start)) { + const stopId = lifelineData.start[startId]; + if (!stopId) { + lifelineData.start[startId] = startId; + lifelineData.stop[startId] = startId; + } + } + } + + return oData; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Preprocess model, adding IDs and whatnot. + * @param original to be preprocessed. + * @returns preprocessed JSON. + * @private + */ + _preprocess(original) { + + const json = _merge({}, this.templates.defaultModel, original); + const metamodel = this.metamodel.unwrap(); + if (!json.diagram.metadata.ref) { + if (metamodel.diagram.metadata.id) { + json.diagram.metadata.ref = metamodel.diagram.metadata.id; + } else { + json.diagram.metadata.ref = '$'; + } + } + + for (const lifeline of json.diagram.lifelines) { + lifeline.id = lifeline.id || lifeline.name; + } + + for (const step of json.diagram.steps) { + if (step.message) { + step.message.id = step.message.id || Model._guid(); + const occurrences = step.message.occurrences; + if (occurrences) { + occurrences.start = occurrences.start || []; + occurrences.stop = occurrences.stop || []; + } + } + } + + if (!json.diagram.metadata.id || json.diagram.metadata.id === '$') { + json.diagram.metadata.id = Model._guid(); + } + + return json; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Create pseudo-UUID. + * @returns {string} + * @private + */ + static _guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return `${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}`; + } + +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js new file mode 100644 index 0000000000..4130ec7ec3 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js @@ -0,0 +1,110 @@ +/*! + * 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. + */ + +/** + * Example scenarios, for development, testing and demos. + */ +export default class Scenarios { + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct scenarios; read model and metamodel templates. + */ + constructor() { + this.templates = { + model: { + ecomp: require('./model/ECOMP.json'), + blank: require('./model/BLANK.json'), + dimensions: require('./model/DIMENSIONS.json'), + }, + metamodel: { + ecomp: require('./metamodel/ECOMP.json'), + blank: require('./metamodel/BLANK.json'), + }, + }; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get ECOMP scenario. + * @return ECOMP scenario JSON. + */ + getECOMP() { + return JSON.parse(JSON.stringify(this.templates.model.ecomp)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get ECOMP scenario metamodel. + * @return scenario metamodel JSON. + */ + getECOMPMetamodel() { + return JSON.parse(JSON.stringify(this.templates.metamodel.ecomp)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get blank scenario. + * @return blank scenario JSON. + */ + getBlank() { + return JSON.parse(JSON.stringify(this.templates.model.blank)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get empty scenario metamodel. + * @return empty metamodel JSON. + */ + getBlankMetamodel() { + return JSON.parse(JSON.stringify(this.templates.metamodel.blank)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get scenario. + * @return scenario JSON. + */ + getDimensions() { + return JSON.parse(JSON.stringify(this.templates.model.dimensions)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get scenario metamodel. + * @return metamodel JSON. + */ + getDimensionsMetamodel() { + return JSON.parse(JSON.stringify(this.templates.metamodel.blank)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get demo metamodels. + * @returns {*[]} + */ + getMetamodels() { + return [this.getBlankMetamodel(), this.getDimensionsMetamodel(), this.getECOMPMetamodel()]; + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/BLANK.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/BLANK.json new file mode 100644 index 0000000000..2853405883 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/BLANK.json @@ -0,0 +1,16 @@ +{ + "diagram": { + "metadata": { + "id": "BLANK", + "name": "Blank" + }, + "lifelines": { + "lifelines": [], + "constraints": { + "create": true, + "delete": true, + "reorder": true + } + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/DIMENSIONS.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/DIMENSIONS.json new file mode 100644 index 0000000000..f02111d0f3 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/DIMENSIONS.json @@ -0,0 +1,16 @@ +{ + "diagram": { + "metadata": { + "id": "DIMENSIONS", + "name": "Dimensions" + }, + "lifelines": { + "lifelines": [], + "constraints": { + "create": true, + "delete": true, + "reorder": true + } + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/ECOMP.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/ECOMP.json new file mode 100644 index 0000000000..939c1398b5 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/metamodel/ECOMP.json @@ -0,0 +1,62 @@ +{ + "diagram": { + "metadata": { + "id": "ECOMP", + "name": "ECOMP" + + }, + "lifelines": { + "lifelines": [{ + "id": "1", + "name": "Customer" + }, { + "id": "2", + "name": "MSO" + }, { + "id": "3", + "name": "SDN" + }, { + "id": "4", + "name": "A&AI" + }, { + "id": "5", + "name": "IPE TOR" + }, { + "id": "6", + "name": "ORM" + }, { + "id": "7", + "name": "ORD" + }, { + "id": "8", + "name": "Heat" + }, { + "id": "9", + "name": "NovaAPI" + }, { + "id": "10", + "name": "Ntrn Contrl" + }, { + "id": "11", + "name": "RO" + }, { + "id": "12", + "name": "Nova Agent" + }, { + "id": "13", + "name": "VF Agent" + }, { + "id": "14", + "name": "Hypervisor" + }, { + "id": "15", + "name": "VF" + }], + "constraints": { + "create": true, + "delete": true, + "reorder": true + } + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/BLANK.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/BLANK.json new file mode 100644 index 0000000000..784a80e820 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/BLANK.json @@ -0,0 +1,37 @@ +{ + "diagram": { + "metadata": { + "id": "$", + "ref": "BLANK", + "name": "New Sequence" + }, + "lifelines": [ + { + "id": "Alice", + "name": "Alice" + }, + { + "id": "Bob", + "name": "Bob" + } + ], + "steps": [ + { + "message": { + "from": "Alice", + "to": "Bob", + "label": "Sup Bob", + "type": "request" + } + }, + { + "message": { + "from": "Bob", + "to": "Alice", + "label": "Yo Alice", + "type": "response" + } + } + ] + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/DIMENSIONS.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/DIMENSIONS.json new file mode 100644 index 0000000000..642e34a785 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/DIMENSIONS.json @@ -0,0 +1,91 @@ +{ + "diagram": { + "metadata": { + "id": "DIMENSIONS1", + "name": "Dimensions Test", + "ref": "DIMENSIONS" + }, + "lifelines": [ + { + "id": "L01", + "name": "Lorum Ipsum" + }, + { + "id": "L02", + "name": "Donec nisi urna, porttitor efficitur felis vel, efficitur consequat nunc" + }, + { + "id": "L03", + "name": "Mauris dignissim SphymomanometerSphymomanometer enim non sapien tristique lacinia" + } + ], + "steps": [ + { + "message": { + "id": "M01", + "from": "L01", + "to": "L02", + "name": "Morbi", + "type": "request", + "notes": [ + "Proin non libero malesuada." + ], + "fragment": { + "operator": "alt", + "start": true, + "guard": "Curabitur sollicitudin nulla elit, et ultrices tortor faucibus quis" + }, + "occurrences": { + "start": ["L01", "L02"], + "stop": [] + } + } + }, + { + "message": { + "id": "M02", + "from": "L02", + "to": "L03", + "name": "Quisque pretium tellus sit amet congue dictum. Mauris ac rutrum arcu, et fringilla orci", + "type": "request", + "notes": [ + "Nam quis felis hendrerit, lacinia ipsum vitae, faucibus elit. Morbi sit amet nunc eget massa vehicula rhoncus sit amet vel tellus. Aliquam accumsan eros elit, et sollicitudin lacus vehicula eu. Aenean rhoncus justo ut felis tincidunt, sit amet vulputate metus aliquet. Phasellus tellus est, consequat nec ex mollis, lacinia vestibulum justo. Nam quis felis hendrerit, lacinia ipsum vitae, faucibus elit. Morbi sit amet nunc eget massa vehicula rhoncus sit amet vel tellus. Aliquam accumsan eros elit, et sollicitudin lacus vehicula eu. Aenean rhoncus justo ut felis tincidunt, sit amet vulputate metus aliquet. Phasellus tellus est, consequat nec ex mollis, lacinia vestibulum justo." + ], + "occurrences": { + "start": [], + "stop": ["L02"] + } + } + }, + { + "message": { + "id": "M03", + "from": "L01", + "to": "L03", + "name": "Nullam", + "type": "response", + "fragment": { + "stop": true + }, + "occurrences": { + "start": [], + "stop": ["L01"] + } + } + }, + { + "message": { + "id": "M04", + "from": "L01", + "to": "L03", + "name": "Etiam convallis augue est. ", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + } + ] + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/ECOMP.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/ECOMP.json new file mode 100644 index 0000000000..dd9bfc5eb0 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/model/ECOMP.json @@ -0,0 +1,514 @@ +{ + "diagram": { + "metadata": { + "id": "ECOMP1", + "name": "Detailed flow in Ecomp1", + "ref": "ECOMP" + }, + "lifelines": [ + { + "id": "L01", + "name": "Customer" + }, + { + "id": "L02", + "name": "MSO" + }, + { + "id": "L03", + "name": "SDN" + }, + { + "id": "L04", + "name": "A&AI" + }, + { + "id": "L05", + "name": "IPE TOR" + }, + { + "id": "L06", + "name": "ORM" + }, + { + "id": "L07", + "name": "ORD" + }, + { + "id": "L08", + "name": "Heat" + }, + { + "id": "L09", + "name": "NovaAPI" + }, + { + "id": "L10", + "name": "Ntrn Contrl" + }, + { + "id": "L11", + "name": "RO" + }, + { + "id": "L12", + "name": "Nova Agent" + }, + { + "id": "L13", + "name": "VF Agent" + }, + { + "id": "L14", + "name": "Hypervisor" + }, + { + "id": "L15", + "name": "VF" + } + ], + "steps": [ + { + "message": { + "id": "M01", + "from": "L01", + "to": "L02", + "name": "Create", + "type": "request", + "notes": [ + "This note is short." + ], + "occurrences": { + "start": ["L01", "L02"], + "stop": [] + } + } + }, + { + "message": { + "id": "M02", + "from": "L02", + "to": "L04", + "name": "Check Tenant", + "type": "request", + "occurrences": { + "start": ["L02"], + "stop": [] + } + } + }, + { + "message": { + "id": "M03", + "from": "L02", + "to": "L06", + "name": "Create Tenant", + "type": "request", + "fragment": { + "operator": "alt", + "start": true, + "guard": "Does not exist" + }, + "occurrences": { + "start": ["L06"], + "stop": [] + } + } + }, + { + "message": { + "id": "M04", + "from": "L06", + "to": "L07", + "name": "Distribute", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M05", + "from": "L06", + "to": "L02", + "name": "Async Response", + "type": "response", + "asynchronous": true, + "fragment": { + "stop": true + }, + "occurrences": { + "start": [], + "stop": ["L02", "L06"] + } + } + }, + { + "message": { + "id": "M06", + "from": "L07", + "to": "L08", + "name": "Push", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M07", + "from": "L08", + "to": "L02", + "name": "Tenant Complete", + "type": "response", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M08", + "from": "L02", + "to": "L03", + "name": "Service Topology", + "type": "request", + "occurrences": { + "start": ["L03"], + "stop": [] + } + } + }, + { + "message": { + "id": "M09", + "from": "L03", + "to": "L05", + "name": "Pre-configs", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M10", + "from": "L03", + "to": "L04", + "name": "Retrieve and populate", + "type": "request", + "occurrences": { + "start": [], + "stop": ["L03"] + } + } + }, + { + "message": { + "id": "M11", + "from": "L02", + "to": "L08", + "name": "VNF PreRequisite Heat Template", + "type": "request", + "notes": [ + "I got up and made coffee and read my emails and answered them until I got frustrated and made a mental note to answer the others later and then looked out of the window for a while and then made more coffee." + ], + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M12", + "from": "L08", + "to": "L10", + "name": "Provider and OAM nw", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M13", + "from": "L02", + "to": "L08", + "name": "Get Stack Status", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M14", + "from": "L08", + "to": "L02", + "name": "Status complete", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M15", + "from": "L11", + "to": "L04", + "name": "Provider and OAM Inventory", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M16", + "from": "L02", + "to": "L08", + "name": "VNF Server Heat Template", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M17", + "from": "L08", + "to": "L10", + "name": "Show Port", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M18", + "from": "L11", + "to": "L02", + "name": "Async Response with Stack ID", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M19", + "from": "L10", + "to": "L08", + "name": "Response", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M20", + "from": "L08", + "to": "L09", + "name": "Nova VM", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M21", + "from": "L09", + "to": "L12", + "name": "Scheduler Picks Nova Agent", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M22", + "from": "L12", + "to": "L14", + "name": "Picks VF", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M23", + "from": "L12", + "to": "L10", + "name": "Retrieves Port Info", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M24", + "from": "L12", + "to": "L13", + "name": "Calls CF Agent", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M25", + "from": "L13", + "to": "L15", + "name": "Configure VF", + "type": "response", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M26", + "from": "L15", + "to": "L13", + "name": "Response", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M27", + "from": "L13", + "to": "L12", + "name": "Complete", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M28", + "from": "L12", + "to": "L08", + "name": "Response Complete", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M29", + "from": "L11", + "to": "L04", + "name": "VServer and Show Port Inventory", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M30", + "from": "L02", + "to": "L08", + "name": "Get Stack Status", + "type": "request", + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M31", + "from": "L08", + "to": "L02", + "name": "Stack Status Complete", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": [] + } + } + }, + { + "message": { + "id": "M32", + "from": "L02", + "to": "L01", + "name": "Done", + "type": "response", + "asynchronous": true, + "occurrences": { + "start": [], + "stop": ["L01", "L02"] + } + } + } + ] + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc-sequencer-meta-schema.xsd b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc-sequencer-meta-schema.xsd new file mode 100644 index 0000000000..f75063bed5 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc-sequencer-meta-schema.xsd @@ -0,0 +1,166 @@ + + + + + + + + + Diagram meta-schema, defining what diagram documents may look like. + + The main difference between the metaschema (this) and the schema, is that + the metaschema describes what's *allowed* rather than what *is*. + + Specific differences: + + 1. The metaschema exists primarily to constrain lifelines; to declare any + that are predefined, to prescribe cardinality, order and whether or not + ad hoc lifelines may be created by the user. + 2. The metaschema doesn't constrain messages at all. This may come along later, + but for now they're freetext, and can be defined between any legal pair + of lifelines. + 3. The metaschema doesn't have @ref attributes; its @id attributes are the + target of @ref attributes in the instance schema.m + + + + + + + + + + + + + + + + + Common attributes, most importantly @id, which every entity must have. + + + + + + + + + + + + + + + Schema definition identifier. + + + + + + + Human-readable name. + + + + + + + + + + + Diagram metadata, including: + - Unique ID, referenced by @ref attributes in instance documents. + - Human-readable description, displayed on-screen. + + + + + + + + + + + + + Metadata concerning a single lifeline. + + + + + + + + Whether an instance may omit this lifeline. Only takes effect + where the lifelines setting is @delete=true. + + + + + + + + + + + + + Metadata concerning allowed lifelines. Somewhat more strict that + instance data. + + + + + + + + + + + + Whether the user may create their own lifelines. + + + + + + + Whether declared lifelines may be deleted. + See also @mandatory on lifeline. + + + + + + + Whether lifelines may be reordered. + + + + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc-sequencer-schema.xsd b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc-sequencer-schema.xsd new file mode 100644 index 0000000000..71a7d07cb1 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc-sequencer-schema.xsd @@ -0,0 +1,274 @@ + + + + + + + + + + Diagram state. + + + + + + + + + + + + + + + + + Stuff common to all entities; an identifier, a name, an optional + schema reference, and some optional notes. + + + + + + + + + + + + + + Optional annotations; non-structural information attached to any entity. + + + + + + + + + + + + + Entity identifier. + + + + + + + Optional reference to schema definition, where this entity + corresponds to (and is constrained by) a schema entity. + + + + + + + Human-readable name. + + + + + + + ID of entity in originating system. For external use; not + used by the sequencer widget. + + + + + + + + + + + + Diagram metadata, including name, identifier and schema reference. + + + + + + + + + + + + + Definition of a single lifeline. + + + + + + + + + + + + + A set of lifelines. May be top-level or in a fragment. + + + + + + + + + + + + + + + + + An occurrence at one or other end of a message. + + + + + + + + + + + + + + + + + + + + A fragment directive. + + + + + + Whether fragment starts; fragment activated when @start=true. + + + + + + + Indication of the last message in this fragment. + + + + + + + Fragment operation. Start with the three everybody knows, but + there are others. + + + + + + + + + + + + + + Guard condition. + + + + + + + + + + + A message between lifelines. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Guard condition within a fragment. Some fragments have more than + one section, each with their own guard condition. + + + + + + + + + + + + + + An ordered set of messages and subsequences. + + + + + + + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc_sequencer_meta_schema.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc_sequencer_meta_schema.json new file mode 100644 index 0000000000..cf4174ed35 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc_sequencer_meta_schema.json @@ -0,0 +1,332 @@ + +{ + "id":"#", + "definitions":{ + "LifelinesType.Constraints":{ + "type":"object", + "title":"LifelinesType.Constraints", + "required":[ + "create", + "delete", + "reorder" + ], + "properties":{ + "create":{ + "title":"create", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/boolean" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"create", + "namespaceURI":"" + } + }, + "delete":{ + "title":"delete", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/boolean" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"delete", + "namespaceURI":"" + } + }, + "reorder":{ + "title":"reorder", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/boolean" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"reorder", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "propertiesOrder":[ + "create", + "delete", + "reorder" + ] + }, + "LifelinesType":{ + "required":[ + "constraints" + ], + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"LifelinesType", + "properties":{ + "lifeline":{ + "title":"lifeline", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"#/definitions/LifelineType" + }, + "minItems":0 + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"lifeline", + "namespaceURI":"" + } + }, + "constraints":{ + "title":"constraints", + "allOf":[ + { + "$ref":"#/definitions/LifelinesType.Constraints" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"constraints", + "namespaceURI":"" + } + } + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"lifelinesType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "lifeline", + "constraints" + ] + }, + "EntityType.Notes":{ + "type":"object", + "title":"EntityType.Notes", + "properties":{ + "note":{ + "title":"note", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + }, + "minItems":0 + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"note", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "propertiesOrder":[ + "note" + ] + }, + "MetadataType":{ + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"MetadataType", + "properties":{ + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"metadataType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + } + }, + "EntityType":{ + "type":"object", + "title":"EntityType", + "required":[ + "id", + "name" + ], + "properties":{ + "notes":{ + "title":"notes", + "allOf":[ + { + "$ref":"#/definitions/EntityType.Notes" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"notes", + "namespaceURI":"" + } + }, + "id":{ + "title":"id", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"id", + "namespaceURI":"" + } + }, + "name":{ + "title":"name", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"name", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "typeName":{ + "localPart":"entityType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "notes", + "id", + "name" + ] + }, + "Diagram":{ + "type":"object", + "title":"Diagram", + "required":[ + "metadata", + "lifelines" + ], + "properties":{ + "metadata":{ + "title":"metadata", + "allOf":[ + { + "$ref":"#/definitions/MetadataType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"metadata", + "namespaceURI":"" + } + }, + "lifelines":{ + "title":"lifelines", + "allOf":[ + { + "$ref":"#/definitions/LifelinesType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"lifelines", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "propertiesOrder":[ + "metadata", + "lifelines" + ] + }, + "LifelineType":{ + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"LifelineType", + "properties":{ + "mandatory":{ + "title":"mandatory", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/boolean" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"mandatory", + "namespaceURI":"" + } + } + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"lifelineType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "mandatory" + ] + } + }, + "anyOf":[ + { + "type":"object", + "properties":{ + "name":{ + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/QName" + }, + { + "type":"object", + "properties":{ + "localPart":{ + "enum":[ + "diagram" + ] + }, + "namespaceURI":{ + "enum":[ + "http://ns.ecomp.com/asdc/sequencer" + ] + } + } + } + ] + }, + "value":{ + "$ref":"#/definitions/Diagram" + } + }, + "elementName":{ + "localPart":"diagram", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + } + } + ] +} \ No newline at end of file diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc_sequencer_schema.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc_sequencer_schema.json new file mode 100644 index 0000000000..d655826290 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/schema/asdc_sequencer_schema.json @@ -0,0 +1,582 @@ + +{ + "id":"#", + "definitions":{ + "EntityType.Notes":{ + "type":"object", + "title":"EntityType.Notes", + "properties":{ + "note":{ + "title":"note", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + }, + "minItems":0 + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"note", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "propertiesOrder":[ + "note" + ] + }, + "GuardType":{ + "type":"object", + "title":"GuardType", + "required":[ + "guard", + "steps" + ], + "properties":{ + "guard":{ + "title":"guard", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"guard", + "namespaceURI":"" + } + }, + "steps":{ + "title":"steps", + "allOf":[ + { + "$ref":"#/definitions/StepsType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"steps", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "typeName":{ + "localPart":"guardType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "guard", + "steps" + ] + }, + "MetadataType":{ + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"MetadataType", + "properties":{ + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"metadataType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + } + }, + "OccurrencesType":{ + "type":"object", + "title":"OccurrencesType", + "properties":{ + "start":{ + "title":"start", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"start", + "namespaceURI":"" + } + }, + "stop":{ + "title":"stop", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"stop", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "typeName":{ + "localPart":"occurrencesType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "start", + "stop" + ] + }, + "Diagram":{ + "type":"object", + "title":"Diagram", + "required":[ + "metadata", + "lifelines", + "steps" + ], + "properties":{ + "metadata":{ + "title":"metadata", + "allOf":[ + { + "$ref":"#/definitions/MetadataType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"metadata", + "namespaceURI":"" + } + }, + "lifelines":{ + "title":"lifelines", + "allOf":[ + { + "$ref":"#/definitions/LifelinesType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"lifelines", + "namespaceURI":"" + } + }, + "steps":{ + "title":"steps", + "allOf":[ + { + "$ref":"#/definitions/StepsType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"steps", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "propertiesOrder":[ + "metadata", + "lifelines", + "steps" + ] + }, + "LifelineType":{ + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"LifelineType", + "properties":{ + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"lifelineType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + } + }, + "LifelinesType":{ + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"LifelinesType", + "properties":{ + "lifeline":{ + "title":"lifeline", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"#/definitions/LifelineType" + }, + "minItems":0 + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"lifeline", + "namespaceURI":"" + } + } + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"lifelinesType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "lifeline" + ] + }, + "FragmentType":{ + "type":"object", + "title":"FragmentType", + "properties":{ + "start":{ + "title":"start", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/boolean" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"start", + "namespaceURI":"" + } + }, + "stop":{ + "title":"stop", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"stop", + "namespaceURI":"" + } + }, + "operation":{ + "title":"operation", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"operation", + "namespaceURI":"" + } + }, + "guard":{ + "title":"guard", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"guard", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "typeName":{ + "localPart":"fragmentType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "start", + "stop", + "operation", + "guard" + ] + }, + "StepsType":{ + "required":[ + "message" + ], + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"StepsType", + "properties":{ + "message":{ + "title":"message", + "allOf":[ + { + "type":"array", + "items":{ + "$ref":"#/definitions/MessageType" + }, + "minItems":1 + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"message", + "namespaceURI":"" + } + } + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"stepsType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "message" + ] + }, + "EntityType":{ + "type":"object", + "title":"EntityType", + "required":[ + "id", + "name" + ], + "properties":{ + "notes":{ + "title":"notes", + "allOf":[ + { + "$ref":"#/definitions/EntityType.Notes" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"notes", + "namespaceURI":"" + } + }, + "id":{ + "title":"id", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"id", + "namespaceURI":"" + } + }, + "ref":{ + "title":"ref", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"ref", + "namespaceURI":"" + } + }, + "name":{ + "title":"name", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"name", + "namespaceURI":"" + } + } + }, + "typeType":"classInfo", + "typeName":{ + "localPart":"entityType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "notes", + "id", + "ref", + "name" + ] + }, + "MessageType":{ + "required":[ + "to", + "from" + ], + "allOf":[ + { + "$ref":"#/definitions/EntityType" + }, + { + "type":"object", + "title":"MessageType", + "properties":{ + "occurrences":{ + "title":"occurrences", + "allOf":[ + { + "$ref":"#/definitions/OccurrencesType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"occurrences", + "namespaceURI":"" + } + }, + "fragment":{ + "title":"fragment", + "allOf":[ + { + "$ref":"#/definitions/FragmentType" + } + ], + "propertyType":"element", + "elementName":{ + "localPart":"fragment", + "namespaceURI":"" + } + }, + "to":{ + "title":"to", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"to", + "namespaceURI":"" + } + }, + "from":{ + "title":"from", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"from", + "namespaceURI":"" + } + }, + "type":{ + "title":"type", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/string" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"type", + "namespaceURI":"" + } + }, + "asynchronous":{ + "title":"asynchronous", + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/boolean" + } + ], + "propertyType":"attribute", + "attributeName":{ + "localPart":"asynchronous", + "namespaceURI":"" + } + } + } + } + ], + "typeType":"classInfo", + "typeName":{ + "localPart":"messageType", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + }, + "propertiesOrder":[ + "occurrences", + "fragment", + "to", + "from", + "type", + "asynchronous" + ] + } + }, + "anyOf":[ + { + "type":"object", + "properties":{ + "name":{ + "allOf":[ + { + "$ref":"http://www.jsonix.org/jsonschemas/w3c/2001/XMLSchema.jsonschema#/definitions/QName" + }, + { + "type":"object", + "properties":{ + "localPart":{ + "enum":[ + "diagram" + ] + }, + "namespaceURI":{ + "enum":[ + "http://ns.ecomp.com/asdc/sequencer" + ] + } + } + } + ] + }, + "value":{ + "$ref":"#/definitions/Diagram" + } + }, + "elementName":{ + "localPart":"diagram", + "namespaceURI":"http://ns.ecomp.com/asdc/sequencer" + } + } + ] +} \ No newline at end of file diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/templates/default.metamodel.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/templates/default.metamodel.json new file mode 100644 index 0000000000..f6a28a8723 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/templates/default.metamodel.json @@ -0,0 +1,17 @@ +{ + "diagram": { + "metadata": { + "id": "$", + "name": "Blank Sequence" + + }, + "lifelines": { + "lifelines": [], + "constraints": { + "create": true, + "delete": true, + "reorder": true + } + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/templates/default.model.json b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/templates/default.model.json new file mode 100644 index 0000000000..42edc5516b --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/templates/default.model.json @@ -0,0 +1,11 @@ +{ + "diagram": { + "metadata": { + "id": "$", + "name": "New Sequence" + + }, + "lifelines": [], + "steps": [] + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx new file mode 100644 index 0000000000..33a62f7228 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx @@ -0,0 +1,35 @@ +/*! + * 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 { render } from 'react-dom'; +import Sequencer from './ecomp/asdc/sequencer/Sequencer'; +import '../res/ecomp/asdc/sequencer/sequencer-development.scss'; +import '../res/thirdparty/react-select/react-select.min.css'; + +function renderApplication() { + const shell = document.createElement('div'); + shell.setAttribute('style', 'height:100%;width:100%;margin:0;padding:0'); + document.body.appendChild(shell); + const options = { demo: true }; + render(, shell); +} + +if (window.addEventListener) { + window.addEventListener('DOMContentLoaded', renderApplication); +} else { + window.attachEvent('onload', renderApplication); +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/_typography.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/_typography.scss new file mode 100644 index 0000000000..5d23eff34f --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/_typography.scss @@ -0,0 +1,25 @@ +/* Fonts */ +@font-face { + font-family: Omnes-Light; + src: url('ecomp/asdc/common/fonts/omnes-att-light.otf'); +} + +@font-face { + font-family: Omnes-Regular; + src: url('ecomp/asdc/common/fonts/omnes-att-regular.otf'); +} + +@font-face { + font-family: Omnes-Medium; + src: url('ecomp/asdc/common/fonts/omnes-att-medium.otf'); +} + +@font-face { + font-family: Omnes-Bold; + src: url('ecomp/asdc/common/fonts/omnes-att-bold.otf'); +} + +$base-font-regular: omnes-regular, "Omnes-Regular"; +$base-font-light: omnes-light, "Omnes-Light"; +$base-font-medium: omnes-medium, "Omnes-Medium"; +$base-font-bold: omnes-bold, "Omnes-Bold"; diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/_variables.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/_variables.scss new file mode 100644 index 0000000000..1880ac3047 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/_variables.scss @@ -0,0 +1,44 @@ + +// primary colors +$blue: #009fdb; +$dark-blue: #0568ae; +$light-blue: #71c5e8; +$green: #4ca90c; +$dark-green: #007a3e; +$light-green: #b5bd00; +$orange: #ea7400; +$yellow: #ffb81c; +$dark-purple: #702f8a; +$purple: #9063cd; +$light-purple: #caa2dd; +$black: #000000; +$dark-gray: #5a5a5a; +$gray: #959595; +$light-gray: #d2d2d2; +$white: #ffffff; + +// Secondary Colors +$red: #cf2a2a; +$background-gray: #f2f2f2; +$text-black: #191919; +$link-blue: #056bae; +$functional-green: #007a3e; +$functional-yellow: #ffb81c; +$tlv-gray: #f8f8f8; +$tlv-light-gray: #eaeaea; +$tlv-hover: #e6f6fb; + + +$scroll-bar-color: $dark-gray; + +//responsive @media params +$tablet-max-width: 1024px; +$laptop-min-width: 1224px; +$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"; diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-bold-italic.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-bold-italic.otf new file mode 100644 index 0000000000..77f0dbc15f Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-bold-italic.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-bold.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-bold.otf new file mode 100644 index 0000000000..136afca84c Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-bold.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-italic.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-italic.otf new file mode 100644 index 0000000000..5dc1da79d4 Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-italic.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-light-Italic.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-light-Italic.otf new file mode 100644 index 0000000000..b13ae4fede Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-light-Italic.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-light.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-light.otf new file mode 100644 index 0000000000..587d871e6f Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-light.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-medium-italic.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-medium-italic.otf new file mode 100644 index 0000000000..f824bc23e7 Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-medium-italic.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-medium.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-medium.otf new file mode 100644 index 0000000000..3085c1fa39 Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-medium.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-regular.otf b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-regular.otf new file mode 100644 index 0000000000..a1a78eb7ca Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/common/fonts/omnes-att-regular.otf differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-actions.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-actions.scss new file mode 100644 index 0000000000..bc5b151c16 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-actions.scss @@ -0,0 +1,120 @@ +.asdcs-control { + + .asdcs-actions { + + @include drop-shadow(); + + * { + @include noselect(); + } + + $asdcsActionMenuWidth: 275px; + $asdcsActionMenuSpanIndent: 20px; + + position: fixed; + display: none; + top: 0; + left: 0; + + background: $asdcsColorOneLight; + border: 1px solid $asdcsColorWhitish; + border-radius: $asdcsPopupBorderRadius; + width: $asdcsActionMenuWidth; + z-index: 10000; + + .asdcs-actions-header { + padding: 5px; + height: 20px; + pointer-events: none; + .asdcs-actions-icon { + height: $asdcsSmallIconSize; + width: $asdcsSmallIconSize; + fill: $asdcsColorTwo; + } + } + + .asdcs-actions-footer { + height: 10px; + } + + div.asdcs-actions-options { + + div.asdcs-actions-state { + float: right; + height: $asdcsSmallIconSize; + width: $asdcsSmallIconSize; + .asdcs-actions-icon { + fill: $asdcsColorOneDark; + } + } + + .asdcs-icon-action { + display: none; + fill: $asdcsColorOneDark; + height: 20px; + width: 20px; + } + + span.asdcs-annotation { + color: $asdcsColorOneDark; + } + + .asdcs-actions-option { + padding: 8px; + border-top: 1px solid $asdcsColorOne; + border-collapse: collapse; + cursor: pointer; + &:hover { + background-color: lighten($asdcsColorOneLight, 5%); + @include transition(background-color $asdcsTransitionTime ease); + } + } + + .asdcs-icon { + fill: $asdcsColorOneDark; + &:hover { + fill: $asdcsColorEmphasisHover; + @include transition(fill $asdcsTransitionTime ease); + } + } + + .asdcs-actions-optiongroup-occurrence { + .asdcs-actions-option-occurrence-from, + .asdcs-actions-option-occurrence-to { + span.asdcs-label { + padding-left: $asdcsActionMenuSpanIndent; + } + } + .asdcs-actions-option-occurrence-toggle { + background-color: $asdcsColorOneLight; + } + } + + .asdcs-actions-optiongroup-fragment { + .asdcs-actions-fragment-toggle { + background-color: $asdcsColorOneLight; + } + } + + .asdcs-actions-fragment-operator, + .asdcs-actions-fragment-guard { + div.asdcs-label { + display: table-cell; + width: 100px; + } + div.asdcs-value { + display: table-cell; + width: 150px; + input { + width: 90%; + border-color: $asdcsColorOne; + } + .asdcs-editable-select { + border: 1px solid $asdcsColorOne; + } + } + } + } + } + +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-common.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-common.scss new file mode 100644 index 0000000000..e90a435a98 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-common.scss @@ -0,0 +1,112 @@ +@import '../common/_variables.scss'; + +$asdcsApplicationMinHeight: 400px; +$asdcsEditorWidth: 500px; + +$asdcsFontSize: 13px; +$asdcsTransitionTime: 250ms; +$asdcsTextColor: $text-black; + +$asdcsColorWhite: $white; +$asdcsColorWhitish: lighten($asdcsColorWhite, 5%); +$asdcsColorBlack: $black; + +$asdcsColorOne: $tlv-light-gray; +$asdcsColorOneLight: lighten($asdcsColorOne, 5%); +$asdcsColorOneDark: $dark-gray; + +$asdcsColorTwo: $blue; +$asdcsColorTwoLight: lighten($asdcsColorTwo, 10%); + +$asdcsColorThree: $light-purple; + +$asdcsColorGrey: $gray; +$asdcsColorDarkGrey: $dark-gray; +$asdcsColorLightGrey: $light-gray; + +$asdcsColorEmphasis: lighten($asdcsColorTwo, 10%); +$asdcsColorEmphasisHover: $asdcsColorTwo; + +$asdcsSmallIconSize: 20px; + +// Buttons. + +$asdcsColorButtonBG: $asdcsColorOneDark; +$asdcsColorButtonBGHover: $asdcsColorOne; +$asdcsColorButtonFG: $asdcsColorWhitish; +$asdcsColorButtonFGHover: $asdcsColorWhite; + +// Input fields. + +$asdcsInputSelectBorderColor: #d8d8d8; +$asdcsInputSelectBorderRadius: 2px; +$asdcsInputSelectFontSize: 14px; +$asdcsInputSelectHeight: 30px; +$asdcsInputSelectArrowColor: $asdcsColorOneDark; + +$asdcsInputTextColor: $asdcsTextColor; +$asdcsInputTextBorderColor: $asdcsColorWhite; +$asdcsInputTextBorderFocusColor: #d8d8d8; +$asdcsInputTextBorderRadius: 2px; +$asdcsInputTextFontSize: $asdcsFontSize; +$asdcsInputTextHeight: 22px; + +$asdcsPopupBorderRadius: 3px; + +// SVG-specific. + +$asdcsSVGSelectableColor: $asdcsColorOne; +$asdcsSVGSelectableOpacity: 0.01; +$asdcsSVGSelectableWidth: 100; + +$asdcsSVGStrokeColor: $gray; +$asdcsSVGStrokeColorLifeline: $asdcsSVGStrokeColor; +$asdcsSVGStrokeColorMessage: #d8d8d8; +$asdcsSVGTextColorMessage: $asdcsColorOneDark; +$asdcsSVGStrokeColorFragment: $asdcsColorOneDark; +$asdcsSVGStrokeWidth: 4; +$asdcsSVGStrokeActiveColor: $asdcsColorBlack; +$asdcsSVGStrokeActiveWidth: $asdcsSVGStrokeWidth + 4; + +// ------------------------------------------------------------------------------------------------- + +@mixin drop-shadow($x: 0px, $y: 0px, $blur: 2px, $spread: 2px, $alpha: 0.25) { + -webkit-box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha); + -moz-box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha); + box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha); +} + +// ------------------------------------------------------------------------------------------------- + +@mixin transition($transition) { + -webkit-transition: $transition; + -moz-transition: $transition; + -ms-transition: $transition; + -o-transition: $transition; +} + +// ------------------------------------------------------------------------------------------------- + +@mixin gradient($startColor: #eee, $endColor: white) { + background-color: $startColor; + background: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)); + background: -webkit-linear-gradient(top, $startColor, $endColor); + background: -moz-linear-gradient(top, $startColor, $endColor); + background: -ms-linear-gradient(top, $startColor, $endColor); + background: -o-linear-gradient(top, $startColor, $endColor); +} + +// ------------------------------------------------------------------------------------------------- + +@mixin noselect() { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE/Edge */ + user-select: none; /* non-prefixed version, currently not supported by any browser */ +} + +.asdcs-hidden { + display: none; +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-controls.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-controls.scss new file mode 100644 index 0000000000..68256b9c3e --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-controls.scss @@ -0,0 +1,72 @@ +.asdcs-control { + + // Input ///////////////////////////////////////////////////////////////////////////////////////// + + input.asdcs-editable { + border: 1px solid $asdcsInputTextBorderColor; + border-radius: $asdcsInputTextBorderRadius; + padding: 3px 5px; + height: $asdcsInputTextHeight; + width: calc(100% - 10px); + color: $asdcsInputTextColor; + background-color: $asdcsColorWhite; + &:focus { + border: 1px solid $asdcsInputTextBorderFocusColor; + background-color: $asdcsColorWhitish; + } + } + + // Select //////////////////////////////////////////////////////////////////////////////////////// + + .asdcs-editable-select { + outline: none; + border-radius: $asdcsInputSelectBorderRadius; + border: 1px solid $asdcsInputSelectBorderColor; + height: $asdcsInputSelectHeight; + div { + border-radius: $asdcsInputSelectBorderRadius; + } + * { + line-height: initial; + text-align: initial; + font-size: $asdcsInputSelectFontSize; + } + .Select-menu-outer { + .Select-option { + padding: 4px; + .is-selected { + background-color: $asdcsColorOneLight; + } + } + } + .Select-value { + height: $asdcsInputSelectHeight - 4px; + padding-left: 5px; + } + .Select-input { + border: none; + height: 1px; + line-height: 1px; + } + .Select-control { + box-shadow: none; + border: none; + } + .Select-control, .Select-placeholder { + border: none; + height: $asdcsInputSelectHeight - 4px; + } + .Select-placeholder { + padding-left: 4px; + } + .Select-arrow-zone { + text-align: center; + overflow: hidden; + padding-top: 4px; + } + &.is-focused { + .Select-control { + } + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-designer.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-designer.scss new file mode 100644 index 0000000000..9caeee5c57 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-designer.scss @@ -0,0 +1,256 @@ +.asdcs-control { + + .asdcs-editor-designer { + + overflow: hidden; + table { + border-spacing: 2px; + } + + $asdcsActiveBorderWidth: 1px; + $asdcsActiveBorderColor: $asdcsColorEmphasis; + .asdcs-designer-accordion { + display: flex; + flex-direction: column; + } + + .asdcs-designer-metadata { + height: 70px; + display: table-cell; + padding: 0 15px; + text-align: left; + vertical-align: middle; + font-size: 16px; + color: $asdcsTextColor; + @include noselect(); + } + + .asdcs-designer-lifelines-container, + .asdcs-designer-steps-container { + flex: 1; + div,span { + @include noselect(); + } + // overflow: hidden; + overflow: auto; + } + + .asdcs-designer-lifelines-container { + padding: 0; + margin: 0; + .asdcs-designer-lifelines { + padding: 0; + .asdcs-designer-lifeline { + background-color: $asdcsColorWhite; + border: $asdcsActiveBorderWidth solid transparent; //$asdcsColorWhite; + border-bottom: $asdcsActiveBorderWidth solid $asdcsColorOne; + .asdcs-designer-lifeline-new { + background-color: $asdcsColorOneLight; + } + &.asdcs-active { + border: $asdcsActiveBorderWidth solid $asdcsColorEmphasis; + } + } + } + } + + .asdcs-designer-steps-container { + padding: 0; + margin: 0; + .asdcs-designer-steps { + padding: 0; + .asdcs-designer-message { + border: $asdcsActiveBorderWidth solid $asdcsColorWhite; + border-bottom: $asdcsActiveBorderWidth solid $asdcsColorOne; + background-color: $asdcsColorWhite; + .asdcs-designer-message-new { + background-color: $asdcsColorOneLight; + } + &.asdcs-active { + border: $asdcsActiveBorderWidth solid $asdcsColorEmphasis; + } + select { + height: 30px; + width: 100%; + background-color: $asdcsColorWhite; + border-radius: $asdcsInputSelectBorderRadius; + border: 1px solid $asdcsInputSelectBorderColor; + -moz-appearance: none; // Just FF. + option { + padding: 10px 0; + font-size: $asdcsFontSize + 2; + } + &.asdcs-designer-select-message-type { + option { + font-size: $asdcsFontSize + 5; + } + } + } + } + } + } + + .asdcs-designer-lifelines-container, + .asdcs-designer-steps-container { + table.asdcs-designer-layout { + width: 100%; + padding: 8px; + tbody, tr { + width: 100%; + } + td { + text-align: center; + vertical-align: middle; + } + &.asdcs-designer-message-row1 { + padding-bottom: 4px; + td:nth-child(3) { + width: 99%; + } + } + &.asdcs-designer-message-row2 { + padding-top: 0; + tr { + td:nth-child(1), td:nth-child(3) { + width: 45%; + } + td:nth-child(2) { + min-width: 70px; + width: 70px; + } + svg { + fill: $asdcsColorOneDark; + } + } + } + &.asdcs-designer-message-new { + tr { + td:nth-child(2) div { + white-space: nowrap; + color: $asdcsColorEmphasis; + cursor: pointer; + &:hover { + color: $asdcsColorEmphasisHover; + } + } + td:nth-child(3) div { + fill: $asdcsColorEmphasis; + cursor: pointer; + &:hover { + fill: $asdcsColorEmphasisHover; + } + } + td:nth-child(4) { + width: 99%; + } + } + } + &.asdcs-designer-lifeline-row1 { + tr { + td:nth-child(3) { + width: 99%; + } + } + } + &.asdcs-designer-lifeline-new { + tr { + td:nth-child(2) div { + white-space: nowrap; + color: $asdcsColorEmphasis; + cursor: pointer; + &:hover { + color: $asdcsColorEmphasisHover; + } + } + td:nth-child(3) div { + fill: $asdcsColorEmphasis; + &:hover { + fill: $asdcsColorEmphasisHover; + } + } + td:nth-child(4) { + width: 99%; + } + } + } + } + } + + .asdcs-designer-placeholder { + padding: 8px; + background-color: $asdcsColorOneLight; + } + + .asdcs-designer-icon { + display: table-cell; + text-align: right; + vertical-align: middle; + cursor: pointer; + fill: $asdcsColorDarkGrey; + min-width: $asdcsSmallIconSize; + width: $asdcsSmallIconSize; + height: $asdcsSmallIconSize; + &.asdcs-active { + fill: $asdcsColorEmphasis; + } + &:hover { + fill: $asdcsColorEmphasisHover; + @include transition(fill $asdcsTransitionTime ease); + } + svg { + pointer-events: none; + width: $asdcsSmallIconSize; + height: $asdcsSmallIconSize; + } + } + + .asdcs-designer-sort { + text-align: center; + cursor: move; + display: table-cell; + } + + .asdcs-designer-lifeline-index, + .asdcs-designer-message-index{ + color: $asdcsTextColor; + } + + button.asdcs-designer-new { + padding: 0 4px; + svg { + width: $asdcsSmallIconSize; + height: $asdcsSmallIconSize; + } + fill: $asdcsColorWhitish; + &:hover { + background-color: $asdcsColorEmphasis; + @include transition(background-color $asdcsTransitionTime ease); + } + } + + h3 { + margin: 0; + font-size: 14px; + line-height: 40px; + vertical-align: middle; + height: 40px; + padding: 0 10px 0 15px; + background-color: $asdcsColorOne; + border-bottom: 1px solid $asdcsColorOneLight; + color: $asdcsColorOneDark; + text-transform: uppercase; + cursor: pointer; + div.asdcs-designer-icon { + padding-top: 7px; + display: inline; + float: right; + fill: $asdcsColorDarkGrey; + &:hover { + fill: $asdcsColorGrey; + @include transition(fill $asdcsTransitionTime ease); + } + } + @include noselect(); + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-development.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-development.scss new file mode 100644 index 0000000000..0d9eef54d2 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-development.scss @@ -0,0 +1,29 @@ +html,body { + margin: 0px; + padding: 0px; + height: 100%; + width: 100%; +} +@font-face { + font-family: Omnes-Light; + src: url('../common/fonts/omnes-att-light.otf'); +} + +@font-face { + font-family: Omnes-Regular; + src: url('../common/fonts/omnes-att-regular.otf'); +} + +@font-face { + font-family: Omnes-Medium; + src: url('../common/fonts/omnes-att-medium.otf'); +} + +@font-face { + font-family: Omnes-Bold; + src: url('../common/fonts/omnes-att-bold.otf'); +} + +* { + font-family: "Omnes-Regular", "Helvetiva Neue", Helvetica, Arial, SansSerif; +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-diagram-svg.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-diagram-svg.scss new file mode 100644 index 0000000000..310c4d0c40 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-diagram-svg.scss @@ -0,0 +1,189 @@ +// Separate to allow for SVG-only styles, for application by Batik +// to exported SVG on the service-side. + +svg { + + text { + text-anchor: middle; + color: $asdcsTextColor; + stroke: none; + } + + g.asdcs-diagram-title { + text { + font-size: 40pt; + } + } + + rect.asdcs-diagram-occurrence { + stroke: $asdcsSVGStrokeColor; + stroke-width: $asdcsSVGStrokeWidth; + fill: $asdcsColorWhite; + } + + g.asdcs-diagram-message-container { + + text.asdcs-diagram-message-label { + fill: $asdcsSVGTextColorMessage; + pointer-events: none; + font-size: 28pt; + } + + rect.asdcs-diagram-message-label-bg { + pointer-events: none; + stroke: none; + fill: $asdcsColorWhite; + fill-opacity: 0.75; + } + + path.asdcs-diagram-message-selectable { + stroke: $asdcsSVGSelectableColor; + stroke-width: $asdcsSVGSelectableWidth; + fill: none; + opacity: $asdcsSVGSelectableOpacity; + cursor: pointer; + } + + path.asdcs-diagram-message { + pointer-events: none; + stroke: $asdcsSVGStrokeColorMessage; + stroke-width: $asdcsSVGStrokeWidth; + fill: none; + &.asdcs-diagram-message-response { + stroke-dasharray: 30, 10; + } + } + + rect.asdcs-diagram-message-bg { + stroke: none; + fill: $asdcsColorWhite; + } + + &.asdcs-active { + path.asdcs-diagram-message { + stroke: $asdcsSVGStrokeActiveColor; + stroke-width: $asdcsSVGStrokeActiveWidth; + } + rect.asdcs-diagram-message-bg { + // Not FF: + // filter: url(#asdcsSvgHighlight); + } + rect.asdcs-diagram-message-label-bg { + fill: #fafafa; + } + } + + .asdcs-diagram-notes-container { + display: none; + path { + fill:none; + stroke: $asdcsSVGStrokeColor; + stroke-width: 6px; + } + text { + font-weight: bold; + font-size: 40px; + text-anchor: start; + } + } + + &.asdcs-active { + .asdcs-diagram-notes-container { + display: block; + } + } + + } + + g.asdcs-diagram-lifeline-container { + + rect.asdcs-diagram-lifeline-heading-box { + stroke: $asdcsColorThree; + stroke-width: $asdcsSVGStrokeWidth; + fill: $asdcsColorWhite; + cursor: pointer; + } + + text.asdcs-diagram-lifeline-heading-label { + font-size: 36px; + pointer-events: none; + } + + path.asdcs-diagram-lifeline-selectable { + stroke: $asdcsSVGSelectableColor; + stroke-width: $asdcsSVGSelectableWidth; + opacity: $asdcsSVGSelectableOpacity; + cursor: pointer; + } + + path.asdcs-diagram-lifeline { + pointer-events: none; + fill: $asdcsSVGStrokeColor; + stroke: $asdcsSVGStrokeColorLifeline; + stroke-width: $asdcsSVGStrokeWidth; + stroke-dasharray: 40, 10; + } + + rect.asdcs-diagram-lifeline-bg { + stroke: none; + fill:white; + } + + &.asdcs-active { + rect.asdcs-diagram-lifeline-heading-box, path.asdcs-diagram-lifeline { + stroke: $asdcsSVGStrokeActiveColor; + stroke-width: $asdcsSVGStrokeActiveWidth; + } + rect { + // Not supported by FF: + // filter:url(#asdcsSvgHighlight); + } + } + } + + .asdcs-diagram-fragment { + + rect { + stroke: $asdcsSVGStrokeColorFragment; + stroke-width: $asdcsSVGStrokeWidth; + fill: none; + } + + rect.asdcs-diagram-fragment-guard-bg { + pointer-events: none; + stroke: none; + fill: $asdcsColorWhite; + fill-opacity: 0.75; + } + + text { + font-size: 28pt; + text-anchor: start; + &.asdcs-diagram-fragment-guard { + fill: #888; + } + } + + path { + stroke: $asdcsSVGStrokeColor; + stroke-width: $asdcsSVGStrokeWidth; + fill: none; + } + } + + #asdcsDiagramArrowOpen, #asdcsDiagramArrowClosed, #asdcsDiagramArrowSolid { + stroke: $asdcsSVGStrokeColor; + } + + #asdcsDiagramArrowOpen { + fill:none; + } + + #asdcsDiagramArrowClosed { + fill:white; + } + + #asdcsDiagramArrowSolid { + fill:$asdcsSVGStrokeColor; + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-diagram.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-diagram.scss new file mode 100644 index 0000000000..fe67c9059e --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-diagram.scss @@ -0,0 +1,206 @@ +.asdcs-control { + + div.asdcs-diagram { + + width: auto; + height: 100%; + overflow: hidden; + + @include noselect(); + + div.asdcs-diagram-name { + height: 40px; + line-height: 40px; + font-size: 24px; + text-align: center; + background-color: $asdcsColorOneLight; + @include noselect(); + } + + div.asdcs-diagram-svg { + height: 100%; + } + + div.asdcs-diagram-popup { + + @include drop-shadow(); + + $asdcsDiagramPopupIconPaneSize: 80px; + $asdcsDiagramPopupHeaderHeight: 40px; + $asdcsDiagramPopupFooterHeight: $asdcsDiagramPopupHeaderHeight; + $asdcsDiagramPopupHeight: 200px; + $asdcsDiagramPopupWidth: 320px; + $asdcsDiagramPopupBodyHeight: $asdcsDiagramPopupHeight - ($asdcsDiagramPopupHeaderHeight + $asdcsDiagramPopupFooterHeight); + $asdcsDiagramPopupNotesIconSize: 64px; + + position: absolute; + height: $asdcsDiagramPopupHeight; + width: $asdcsDiagramPopupWidth; + text-overflow: ellipsis; + background: $asdcsColorOneLight; + border: 1px solid $asdcsColorWhitish; + border-radius: $asdcsPopupBorderRadius; + + .asdcs-diagram-popup-header { + font-weight: bold; + font-size: 120%; + padding: 4px 10px; + height: $asdcsDiagramPopupHeaderHeight; + line-height: $asdcsDiagramPopupHeaderHeight; + } + + .asdcs-diagram-popup-body { + + height: $asdcsDiagramPopupBodyHeight; + overflow: hidden; + + .asdcs-icon-popup { + display: table-cell; + text-align: center; + vertical-align: middle; + height: $asdcsDiagramPopupBodyHeight; + width: $asdcsDiagramPopupIconPaneSize; + min-width: $asdcsDiagramPopupIconPaneSize; + svg { + height: $asdcsDiagramPopupNotesIconSize; + width: $asdcsDiagramPopupNotesIconSize; + .asdcs-icon { + fill: $asdcsColorOneDark; + } + } + } + + .asdcs-diagram-notes { + display: table-cell; + width: 100%; + vertical-align: middle; + overflow: hidden; + text-align: left; + padding: 0 15px 5px 0; + margin-right: 10px; + } + } + + .asdcs-diagram-popup-footer { + height: 0; + } + + $asdcsPopupBorderColor1: rgba(242, 242, 242, 0); + $asdcsPopupBorderColor2: rgba(211, 211, 211, 0); + + &.asdcs-diagram-popup-topleft { + + &:after, &:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + bottom: 100%; + left: 50px; + } + + &:after { + border-color: $asdcsPopupBorderColor1; + border-width: 6px; + border-bottom-color: $asdcsColorOneLight; + margin-left: -6px; + } + + &:before { + border-color: $asdcsPopupBorderColor2; + border-width: 7px; + border-bottom-color: $asdcsColorOneLight; + margin-left: -7px; + } + } + + &.asdcs-diagram-popup-topright { + + &:after, &:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + bottom: 100%; + right: 50px; + } + + &:after { + border-color: $asdcsPopupBorderColor1; + border-width: 6px; + border-bottom-color: $asdcsColorOneLight; + margin-left: -6px; + } + + &:before { + border-color: $asdcsPopupBorderColor2; + border-width: 7px; + border-bottom-color: $asdcsColorOneLight; + margin-left: -7px; + } + } + + &.asdcs-diagram-popup-bottomleft { + + &:after, &:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + top: 100%; + left: 50px; + } + + &:after { + border-color: $asdcsPopupBorderColor1; + border-width: 6px; + border-top-color: $asdcsColorOneLight; + margin-left: -6px; + } + + &:before { + border-color: $asdcsPopupBorderColor2; + border-width: 7px; + border-top-color: $asdcsColorOneLight; + margin-left: -7px; + } + } + + &.asdcs-diagram-popup-bottomright { + + &:after, &:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + top: 100%; + right: 50px; + } + + &:after { + border-color: rgba(242, 242, 242, 0); + border-width: 6px; + border-top-color: $asdcsColorOneLight; + margin-left: -6px; + } + + &:before { + border-color: rgba(211, 211, 211, 0); + border-width: 7px; + border-top-color: $asdcsColorOneLight; + margin-left: -7px; + } + } + } + + @import 'sequencer-diagram-svg.scss'; + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-dialog.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-dialog.scss new file mode 100644 index 0000000000..89a6412dd0 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-dialog.scss @@ -0,0 +1,159 @@ +/* +
+
+
+
+
+ + +
+
+*/ + +.asdcs-control { + + $asdcsDialogIconSize: 70px; + $asdcsDialogWidth: 400px; + + .asdcs-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 5; + background-color: white; + opacity: 0.25; + pointer-events: all; + } + + .asdcs-dialog { + + position: fixed; + top: 50%; + left: 50%; + width: $asdcsDialogWidth; + margin-left: -($asdcsDialogWidth/2); + height: 300px; + margin-top: -150px; + background: $asdcsColorOneLight; + border: 1px solid $asdcsColorWhitish; + border-radius: $asdcsPopupBorderRadius; + z-index: 10; + + @include drop-shadow(); + + .asdcs-dialog-header { + position: absolute; + height: 60px; + line-height: 40px; + width: 100%; + box-sizing: border-box; + font-size: 14px; + font-weight: bold; + padding: 10px 15px; + } + + .asdcs-dialog-close { + position: absolute; + top: 15px; + right: 15px; + height: $asdcsSmallIconSize; + width: $asdcsSmallIconSize; + fill: $asdcsColorGrey; + cursor: pointer; + &:hover { + fill: $asdcsColorEmphasis; + } + } + + .asdcs-dialog-buttonbar { + + position: absolute; + height: 50px; + width: 100%; + bottom: 0; + //background-color: $asdcsColorOne; + text-align: right; + + button { + width: 75px; + border-radius: 4px; + border: none; + background-color: $asdcsColorTwo; + color: $asdcsColorWhite; + padding: 7px 10px; + margin: 10px 10px 10px 0px; + &:hover { + background-color: $asdcsColorTwoLight; + } + } + } + + .asdcs-dialog-icon { + position: absolute; + top: 80px; + left: 20px; + height: $asdcsDialogIconSize; + width: $asdcsDialogIconSize; + pointer-events: none; + fill: $asdcsColorOneDark; + + svg { + display: none; + } + + &.asdcs-icon-question { + svg.asdcs-icon-question { + display: block; + } + } + + &.asdcs-icon-exclaim { + svg.asdcs-icon-exclaim { + display: block; + } + } + + &.asdcs-icon-info { + svg.asdcs-icon-info { + display: block; + } + } + + &.asdcs-icon-edit { + svg.asdcs-icon-edit { + display: block; + } + } + } + + .asdcs-dialog-message { + position: absolute; + padding: 10px; + top: 90px; + height: 100px; + left: 100px; + width: $asdcsDialogWidth - 120px; + display: table-cell; + vertical-align: middle; + word-wrap: break-word; + text-overflow: ellipsis; + } + + .asdcs-dialog-text { + position:absolute; + padding: 10px; + top:60px; + height: 150px; + left:100px; + width: $asdcsDialogWidth - 130px; + textarea { + height:100%; + width:100%; + resize: none; + } + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-editor.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-editor.scss new file mode 100644 index 0000000000..da35ba9182 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-editor.scss @@ -0,0 +1,125 @@ +.asdcs-control { + + div.asdcs-editor { + + $asdcsEditorResizeWidth: 6px; + $asdcsEditorToolbarPadding: 4px; + + box-sizing: border-box; + float: left; + height: 100%; + width: $asdcsEditorWidth; + overflow-x: hidden; + overflow-y: auto; + position: relative; + padding-right: $asdcsEditorResizeWidth; + + div.asdcs-editor-toolbar { + + display: none; + + height: 40px; + width: 100%; + + background-color: $asdcsColorOneDark; + text-align: center; + + .asdcs-editor-toolbar-demo { + padding: $asdcsEditorToolbarPadding; + display: inline; + float: left; + } + + .asdcs-editor-toolbar-toggle { + padding: $asdcsEditorToolbarPadding; + display: inline; + float: right; + } + + button { + + &.asdcs-button-mode { + + padding-left: 8px; + padding-right: 8px; + + &.asdcs-button-toggle-left { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + margin-right: -4px; + } + + &.asdcs-button-toggle-right { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + margin-left: -4px; + } + + &.asdcs-button-toggle-center { + border-radius: 0; + border-right: 1px solid lighten($asdcsColorOneDark, 3%); + border-left: 1px solid lighten($asdcsColorOneDark, 3%); + } + + &.asdcs-active { + background-color: $asdcsColorButtonBGHover; + } + + &:hover { + border-color: transparent; + @include transition(border-color $asdcsTransitionTime ease); + } + } + + svg { + height: $asdcsSmallIconSize; + width: $asdcsSmallIconSize; + fill: $asdcsColorWhitish; + } + } + } + + div.asdcs-editor-content { + + height: 100%; + width: 100%; + overflow-x: hidden; + overflow-y: auto; + + div.asdcs-editor-code { + display:none; + height: 100%; + width: 100%; + background-color: $asdcsColorWhitish; + textarea { + height: 100%; + } + .CodeMirror { + height: 100%; + } + } + + div.asdcs-editor-designer { + height: 100%; + width: 100%; + + .asdcs-designer-accordion { + height: 100%; + } + } + + } + + div.asdcs-editor-resize-handle { + background-color: $asdcsColorOne; + position: absolute; + top: 0; + right: 0; + height: 100%; + width: $asdcsEditorResizeWidth; + cursor: e-resize; + @include drop-shadow(); + + } + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-standalone.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-standalone.scss new file mode 100644 index 0000000000..bd13af16e0 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sequencer-standalone.scss @@ -0,0 +1,2 @@ +@import 'sequencer-common.scss'; +@import 'sequencer-diagram-svg.scss'; diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites-raster.scss b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites-raster.scss new file mode 100644 index 0000000000..f23295399b --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites-raster.scss @@ -0,0 +1,30 @@ + +@mixin sprite-width($sprite) { + width: nth($sprite, 3); +} +@mixin sprite-height($sprite) { + height: nth($sprite, 4); +} +@function sprite-width($sprite) { + @return nth($sprite, 3); +} +@function sprite-height($sprite) { + @return nth($sprite, 4); +} +@mixin sprite-position($sprite) { + $sprite-offset-x: nth($sprite, 1); + $sprite-offset-y: nth($sprite, 2); + background-position: $sprite-offset-x $sprite-offset-y; +} +@mixin sprite($sprite, $display: block) { + @include sprite-position($sprite); + background-repeat: no-repeat; + overflow: hidden; + display: $display; + @include sprite-width($sprite); + @include sprite-height($sprite); +} + +.icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAAAmCAYAAABagbwLAAAIkklEQVRoBe2ZCVAUVxrH/yDnMAOOHFFYd1eQ04HgGsEIjjEql3jEAo9UUopuorgeAUUUZY1urMRdSzdeSarcrJLVqKuwJhpNxHAKi6igEUE8QPAWhDkYbtmvH844gyAdHavWqn5V3f3e9773ve5ff+97X8+YdHR0QCjGI2BqPFOCJY6AANTIfiAANTJQs672/APeFIJqVyjPaBefzjfR7xY8VJ+GEeoCUCNA1DchANWnYYS6ANQIEPVNCED1aRih/soAtbKyMsLjvnwTrwTQ37u64S9r12LRvDkvn8gLzvBUHvqC9l7KcJHIBt7eXjBt07wU+8Y0+kp4KPfAjx49Qgcd/+/lV3mot7cPvDw9YC+VoqFBjfLycpwtPq97RheXgXBy6Iei8+chk/nB30+GPvQdUUzt8xdLmJ589Gi4uw6CUlGPX0hWVn5FN15bCQwYAW8vD7S3teDChV8AczP09KOYn+/r8JX5wNrSEpU3KpCekaU189Q1IjwcLgP6Q0FzXyq9jIuXLhnomJmZYfzYcXBx7g+NRoMrV66g8FyRgU5vDd5AFy/+CDHvTUcTTaRQqSEWiyG2EWH/vm/x6aYtbJ7Zc+cjasJo/PRzJkYMD0BzcxPs+kphSUC+3rUbnt4y+Ms8oW7QwNHRCY9am5D88TocS89g453phSQmJGD0yOGor69Ha1sb+tr1RVHxOVhbWUD/m9jRqT+WxsUhbKwcSqUCzS2tcHRwwN3bt7Bx89+RnpWre/aJE6dgefwSSKwtcK+mhu5dAhuRNf59YB/Wb/yc6Y15ezySVyyDnViE2ocPYWktgp1EjBMnjiFh1Tqdrd4qvIC+GTwGcwhmdmYG9h5Mg0rdAKnUHjExszEhPJS8IhuFRcUMoIlJH3iSB26ih7paUYkBzi5ITlqJObNnIYvGr1i9BnX1CoLrg8Sl8ZgQMk4HNHbBQgbzW3pJ6Zk5aGlpg4enJ+bMeh9O9v3IS58gjZ0fy2Ae2L8fJ7NzoWlqhqenN+bNjcG6NatR/UEsLl+rQEDgSCQui0PNnRv49OtvcOPWbYhEYsyYPh3R02ag4vp17E39HjGzZqG1UYU1mzehsuomRDZihIWFY2pkON7JK0Da0R97Y8n6eQGVSGyQk5ONvQcOouhiKUxMTNBBN+vq4YlhPu/DwsLi8WS0vh+1ITU1FYeP/sBkJbSswiMmYoTfYBw4lIpTBWeY/GJJCSIjJqBfv76s/ZrzQEwcL8fpgnzsStmDhqYWNk9FZSVMTM2wOmHJ4zlAkIfQg4YgJysDu/bsg4o8nivXrl2HNW1g8Qs/RPTkCHyyaTuCgoIgJs9cve0LZObm62xwHi0WWcLN1ZXJHBzs0dxQi3RaXU3NzUzGLfnamvu4+6BGN663Ci+gPx07Au7gimyIL9wGu8LHywuTIiPQ2NRkMEcDhYTK6psGMrVGBYVSBaWqwUDeSF5lZdH5Y43/0GGsr7S0FAq1BpYUE7nCbUbcg925X8sAc7LfDeqEkH+6EEpaLX369OHE4AJtQeEZPKiJQmBgAAm2w4eyg6rrV2m1VDEV7enC+XOYt+CctomsnFzMnBqJk8e+R86pPBSeOYvDR37A9h07dDp8KryAcoaWJ6zAlMhQWFA85N6gWq0C5z1uA527mefJ0uyms1uRhJYYVxQqFYicTodbDZrGRjqaoJXaSiSsv4FkBoV0lSolWltbodXhrvUq8rJ7dw1UuzY2fLaevLEGkyNCEBwcjJCQECSvSqIVk4fN275C6eXyrkO6bfMCmpi0BjOnhNFSPoQjx0/g1u07qK2rx+SpUUj+aH63hn+t8CHtvFyxs5VQevTkhXBx09ramuKeSLcpKWgT4oqY5HrsWYy1k9jBnEKQUvmA6XAryI42UFuJLR7W1TGZ9iSXy4H2VmSf6gwFO3d+Be7woljM5b2jgoPwtjwIi1uaEbs0STvsmVfTZ/Y+7hw7ehSqblTin7tTUFJWznZpC3Nz+A6RoY12Ym5ZvmjJSj/OTPjJZJDaiXV229rb4e7ujgGOUt2mdLPqBrgZR40MpAzCHO2kw91Dc0sLAoe/QRuYFPn5/2X2Sul+B7l5wsdjsMEtvhEYRJvXnxE9KYzJv0lJwcyod1i97HIp0v6Thvhly2jPKKOw4W0w9lkNXkAVSiWcnJwwhnJIW0olXFxcsHjRYkwJG0Ne08Fkz5qET197exv2HDqCocMCELcwFr/9jTPzyojwCPzpw7mdJh47blnpRRz67jhGBMmRGL+I6XJp3LToaZQRvEu7tRpbd6awMfn5eWikbGHVyuUY9xZ5JJVhNEfsH2PQlzbbvQfSmMxGbIsF8z5A9JRJrM2dplEW4OXhjivXrulkvVV4LfmtO77Elg1rER8fjzjK/bi41qhpwL/27sd7707Hhs/W4ccAOYtxXF/Xwkm6lZOuvvrfNqzHACcHhFK6EhLa6Tm00+AqPVB7mz1MTZ/YXv/JWvYRMT4kDOPGh7IpuTkUdbVYkvQxS+04YXbWz9j25UD2O8DGv25gXq69l+3btiD/bDEb+/nWbViXvBKrKMVLWrlCZ+/+vdvYsXM3a/M5mejndtyAnv5Tcvfwwhh5MCQ2Nrh1sxr7KAXiyowZMyG1scAX/9gNGX21vOYgxcmMTNanPfn7/4GSZBHbSbUy7hoQEAgzkw7kFZzWF0Mufwv+vkMIdgdK6GsqPTOL4tkoaNRKgy8zbtDIkcEY+rov2yyrq6twMO2wgS1tw4k+BMJDQ+DQTwqVSoHcvHxcKrus7dZdo6Oi6EtpADoojFRUXsd3RztDkU6hS6Xrf0q8gXaxIzQfE+gKlFcMFejxJyAA5c+Kl6YAlBcm/koCUP6seGkKQHlh4q8kAOXPipemAJQXJv5KAlD+rHhpPpXY8xolKPVIQPDQHtE8X4cA9Pm49ThKANojmufr+B95nv72O1nccAAAAABJRU5ErkJggg=='); +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.css.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.css.svg new file mode 100644 index 0000000000..ce02a1a41e --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.css.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.defs.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.defs.svg new file mode 100644 index 0000000000..81d66d69d0 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.defs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.png b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.png new file mode 100644 index 0000000000..546a71c681 Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites.png differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/amdocs.png b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/amdocs.png new file mode 100644 index 0000000000..f560f5be17 Binary files /dev/null and b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/amdocs.png differ diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/request-async.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/request-async.svg new file mode 100644 index 0000000000..11c5086198 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/request-async.svg @@ -0,0 +1,59 @@ + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/request-sync.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/request-sync.svg new file mode 100644 index 0000000000..d7410035a4 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/request-sync.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/response.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/response.svg new file mode 100644 index 0000000000..3429077f21 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/arrow/response.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/blank.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/blank.svg new file mode 100644 index 0000000000..9ecbff0eca --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/blank.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/close.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/close.svg new file mode 100644 index 0000000000..2fd91dea94 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/close.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/collapsed.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/collapsed.svg new file mode 100644 index 0000000000..2865518a49 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/collapsed.svg @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/delete.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/delete.svg new file mode 100644 index 0000000000..2fd91dea94 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/delete.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/download.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/download.svg new file mode 100644 index 0000000000..1cc851b518 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/download.svg @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/edit.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/edit.svg new file mode 100644 index 0000000000..f374e7c8d5 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/edit.svg @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/exclaim.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/exclaim.svg new file mode 100644 index 0000000000..8194d819b1 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/exclaim.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/expanded.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/expanded.svg new file mode 100644 index 0000000000..0de9bffb93 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/expanded.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-default.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-default.svg new file mode 100644 index 0000000000..5c7af09ea3 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-default.svg @@ -0,0 +1,22 @@ + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-start.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-start.svg new file mode 100644 index 0000000000..6ea25b8c70 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-start.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-stop.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-stop.svg new file mode 100644 index 0000000000..47f055c873 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/fragment-stop.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/handle.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/handle.svg new file mode 100644 index 0000000000..c21a7dddb7 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/handle.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/info.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/info.svg new file mode 100644 index 0000000000..04a8b2f205 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/info.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/notes.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/notes.svg new file mode 100644 index 0000000000..29a663f602 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/notes.svg @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-default.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-default.svg new file mode 100644 index 0000000000..a943b4acbc --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-default.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-start.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-start.svg new file mode 100644 index 0000000000..df0e78377d --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-start.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-stop.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-stop.svg new file mode 100644 index 0000000000..9b7aecc04e --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/occurrence-stop.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/open.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/open.svg new file mode 100644 index 0000000000..779c20e471 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/open.svg @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/plus.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/plus.svg new file mode 100644 index 0000000000..7854624a97 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/plus.svg @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/question.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/question.svg new file mode 100644 index 0000000000..9cd19eda9a --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/question.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/save.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/save.svg new file mode 100644 index 0000000000..e71a3a3cae --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/save.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/settings.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/settings.svg new file mode 100644 index 0000000000..255980690a --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/settings.svg @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/upload.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/upload.svg new file mode 100644 index 0000000000..453f8418b9 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/upload.svg @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/validate.svg b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/validate.svg new file mode 100644 index 0000000000..35cc9fa418 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/ecomp/asdc/sequencer/sprites/icon/validate.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss b/dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss new file mode 100644 index 0000000000..bcb01d2253 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss @@ -0,0 +1,87 @@ +@import 'ecomp/asdc/sequencer/sequencer-common.scss'; + +.asdcs-control { + + width: 100%; + height: 100%; + min-height: $asdcsApplicationMinHeight; + + * { + outline: none; + line-height: initial; + text-align: initial; + } + + p, div, span, td, input, select, option { + font-size: $asdcsFontSize; + color: $asdcsColorOneDark; + padding: 0; + margin: 0; + } + + table { + border-collapse: initial; + border-spacing: initial; + } + + // SVG always fills its container. + + svg { + height:100%; + width: 100%; + } + + button { + + border-radius: 4px; + background-color: $asdcsColorButtonBG; + padding: 4px; + border: none; + color: $asdcsColorWhitish; + min-width: 30px; + min-height: 30px; + text-align: center; + vertical-align: middle; + + &:hover { + cursor: pointer; + background-color: $asdcsColorEmphasisHover; + color: $asdcsColorWhite; + @include transition(background-color 250ms ease); + } + + &:disabled { + cursor: default; + background-color:gray; + color:darkGray; + } + + } + + form { + display: none; + } + + ::-webkit-scrollbar { + width: 10px; + } + + ::-webkit-scrollbar-track { + border-radius: 0; + background-color: $asdcsColorOneLight; + } + + ::-webkit-scrollbar-thumb { + border-radius: 6px; + margin:1px; + background-color: $asdcsColorOne; + } +} + +@import 'ecomp/asdc/sequencer/sequencer-controls.scss'; +@import 'ecomp/asdc/sequencer/sequencer-diagram.scss'; +@import 'ecomp/asdc/sequencer/sequencer-editor.scss'; +@import 'ecomp/asdc/sequencer/sequencer-dialog.scss'; +@import 'ecomp/asdc/sequencer/sequencer-designer.scss'; +@import 'ecomp/asdc/sequencer/sequencer-actions.scss'; + diff --git a/dox-sequence-diagram-ui/src/main/webapp/res/thirdparty/react-select/react-select.min.css b/dox-sequence-diagram-ui/src/main/webapp/res/thirdparty/react-select/react-select.min.css new file mode 100644 index 0000000000..f1fa71dec4 --- /dev/null +++ b/dox-sequence-diagram-ui/src/main/webapp/res/thirdparty/react-select/react-select.min.css @@ -0,0 +1 @@ +.Select,.Select-control{position:relative}.Select-arrow-zone,.Select-clear-zone,.Select-loading-zone{text-align:center;cursor:pointer}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none}.Select-control{background-color:#fff;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;height:36px;outline:0;overflow:hidden;width:100%}.is-searchable.is-focused:not(.is-open)>.Select-control,.is-searchable.is-open>.Select-control{cursor:text}.Select-control:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.is-open>.Select-control>.Select-arrow{border-color:transparent transparent #999;border-width:0 5px 5px}.is-focused:not(.is-open)>.Select-control{border-color:#007eff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 0 3px rgba(0,126,255,.1)}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-value.Select--single>.Select-control>.Select-value .Select-value-label,.has-value.is-pseudo-focused.Select--single>.Select-control>.Select-value .Select-value-label{color:#333}.has-value.Select--single>.Select-control>.Select-value a.Select-value-label,.has-value.is-pseudo-focused.Select--single>.Select-control>.Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.has-value.Select--single>.Select-control>.Select-value a.Select-value-label:focus,.has-value.Select--single>.Select-control>.Select-value a.Select-value-label:hover,.has-value.is-pseudo-focused.Select--single>.Select-control>.Select-value a.Select-value-label:focus,.has-value.is-pseudo-focused.Select--single>.Select-control>.Select-value a.Select-value-label:hover{color:#007eff;outline:0;text-decoration:underline}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none;border:0;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:0;line-height:14px;padding:8px 0 12px;-webkit-appearance:none}.Select-loading,.Select-loading-zone{width:16px;position:relative;vertical-align:middle}.is-focused .Select-input>input{cursor:text}.has-value.is-pseudo-focused .Select-input{opacity:0}.Select-control:not(.is-searchable)>.Select-input{outline:0}.Select-loading-zone{display:table-cell}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;display:table-cell;position:relative;vertical-align:middle;width:17px}.Select-clear-zone:hover{color:#D0021B}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select-arrow-zone{display:table-cell;position:relative;vertical-align:middle;width:25px;padding-right:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0}.Select-noresults,.Select-option{box-sizing:border-box;display:block;padding:8px 10px}.Select-arrow-zone:hover>.Select-arrow,.is-open .Select-arrow{border-top-color:#666}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;box-shadow:0 1px 0 rgba(0,0,0,.06);box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;top:100%;width:100%;z-index:1;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option{background-color:#fff;color:#666;cursor:pointer}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;background-color:rgba(0,126,255,.04);color:#333}.Select-option.is-focused{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{color:#999;cursor:default}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);border-radius:2px;border:1px solid rgba(0,126,255,.24);color:#007eff;display:inline-block;font-size:.9em;line-height:1.4;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#007eff;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c2e0ff;border-right:1px solid rgba(0,126,255,.24);padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#d8eafd;background-color:rgba(0,113,230,.08);color:#0071e6}.Select--multi .Select-value-icon:active{background-color:#c2e0ff;background-color:rgba(0,126,255,.24)}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}} diff --git a/dox-sequence-diagram-ui/tools/copy-assets.js b/dox-sequence-diagram-ui/tools/copy-assets.js new file mode 100644 index 0000000000..bc4f4f21c9 --- /dev/null +++ b/dox-sequence-diagram-ui/tools/copy-assets.js @@ -0,0 +1,7 @@ +var copy = require('copy'); + +copy(['**/*.json', '**/*.svg', '**/*.html'], 'lib', { + cwd: './src' +}, function (err, file) { + // exposes the vinyl `file` created when the file is copied +}); \ No newline at end of file diff --git a/dox-sequence-diagram-ui/webpack.config.js b/dox-sequence-diagram-ui/webpack.config.js new file mode 100644 index 0000000000..7b7f4b948c --- /dev/null +++ b/dox-sequence-diagram-ui/webpack.config.js @@ -0,0 +1,99 @@ +var webpack = require('webpack'); +var path = require('path'); + +var PATHS = { + SRC: path.resolve(__dirname, 'src/main/webapp'), + TARGET: path.resolve(__dirname, 'dist') +}; + +var devmode = (process.env.npm_lifecycle_event === 'start'); + +var entry = []; +if (devmode) { + entry.push('babel-polyfill'); + entry.push(path.resolve(PATHS.SRC, 'lib/main.jsx')); +} else { + entry.push(path.resolve(PATHS.SRC, 'lib/ecomp/asdc/sequencer/Sequencer.jsx')); +} + +var config = { + entry: entry, + output: { + path: PATHS.TARGET, + filename: 'index.js', + libraryTarget: 'umd' + }, + resolve: { + extensions: ['', '.js', '.jsx'] + }, + eslint: { + failOnWarning: false, + failOnError: true, + configFile: 'eslintrc.json' + }, + devtool: 'eval-source-map', + module: { + preLoaders: [{ + test: /\.(js|jsx)?$/, + loader: 'eslint-loader', + exclude: /node_modules/ + }], + loaders: [{ + test: /\.(js|jsx)$/, + include: path.join(PATHS.SRC, 'lib'), + loader: 'babel-loader', + exclude: /node_modules/, + query: { + presets: ['es2015', 'react'] + } + }, { + test: /\.css$/, + loaders: ['style', 'css'] + }, { + test: /\.(png|woff|woff2|eot|ttf|otf)$/, + loader: 'url-loader?limit=100000' + }, { + test: /\.scss$/, + include: path.join(PATHS.SRC, 'res'), + loaders: ['style', 'css', 'sass'] + }, { + test: /\.html$/, + include: path.join(PATHS.SRC, 'lib'), + loaders: ['raw'] + }, { + test: /\.json$/, + include: path.join(PATHS.SRC, 'lib'), + loaders: ['json'] + }, { + test: /\.svg$/, + loader: 'svg-sprite?' + JSON.stringify({ + name: '[name]_[hash]', + prefixize: true + }) + }] + }, + externals: (devmode ? {} : { + 'd3': 'd3', + 'lodash': 'lodash', + 'react': 'react', + 'react-dnd': 'react-dnd', + 'react-dnd-html5-backend': 'react-dnd-html5-backend', + 'react-dom': 'react-dom', + 'react-redux': 'react-redux', + 'react-select': 'react-select', + 'redux': 'redux' + }), + devServer: { + port: 4096, + quiet: false, + contentBase: 'src/main/webapp', + proxy: { + '/services/*': { + target: 'http://localhost:38080/asdc-sequencer', + secure: false + } + } + } +}; + +module.exports = config; diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/openecomp-sdc-common-rest/pom.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/openecomp-sdc-common-rest/pom.xml index 764fb84db4..29476eb692 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/openecomp-sdc-common-rest/pom.xml +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/openecomp-sdc-common-rest/pom.xml @@ -16,12 +16,12 @@ - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} diff --git a/openecomp-be/backend/openecomp-sdc-action-manager/pom.xml b/openecomp-be/backend/openecomp-sdc-action-manager/pom.xml index af1652ad50..7817863c20 100644 --- a/openecomp-be/backend/openecomp-sdc-action-manager/pom.xml +++ b/openecomp-be/backend/openecomp-sdc-action-manager/pom.xml @@ -14,7 +14,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -24,7 +24,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} diff --git a/openecomp-be/backend/openecomp-sdc-application-config-manager/pom.xml b/openecomp-be/backend/openecomp-sdc-application-config-manager/pom.xml index 437114404f..b614e2f2db 100644 --- a/openecomp-be/backend/openecomp-sdc-application-config-manager/pom.xml +++ b/openecomp-be/backend/openecomp-sdc-application-config-manager/pom.xml @@ -28,7 +28,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-config-lib ${project.version} diff --git a/openecomp-be/backend/openecomp-sdc-validation-manager/pom.xml b/openecomp-be/backend/openecomp-sdc-validation-manager/pom.xml index 4a7b39aae1..0b9e862fe8 100644 --- a/openecomp-be/backend/openecomp-sdc-validation-manager/pom.xml +++ b/openecomp-be/backend/openecomp-sdc-validation-manager/pom.xml @@ -14,7 +14,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -24,7 +24,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml index 6b98e15c75..6e71dde1d0 100644 --- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml @@ -26,7 +26,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -36,9 +36,9 @@ ${project.version} - org.openecomp.core - openecomp-nosqldb-core - ${project.version} + org.openecomp.sdc + openecomp-nosqldb-core + ${project.version} org.openecomp.sdc @@ -46,12 +46,12 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-tosca-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-common-lib/pom.xml b/openecomp-be/lib/openecomp-common-lib/pom.xml index 78c5e86ec7..1be7a072dd 100644 --- a/openecomp-be/lib/openecomp-common-lib/pom.xml +++ b/openecomp-be/lib/openecomp-common-lib/pom.xml @@ -9,12 +9,12 @@ openecomp-common-lib - org.openecomp.core + openecomp-common-lib - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib 1.0.0-SNAPSHOT diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-config-lib/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-config-lib/pom.xml index a10e9cbd04..0abf23900f 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-config-lib/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-config-lib/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - org.openecomp.core + org.openecomp.sdc openecomp-core-lib 1.0.0-SNAPSHOT @@ -26,7 +26,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-api/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-api/pom.xml index ccb3db833e..22e17f490d 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-api/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-api/pom.xml @@ -11,18 +11,17 @@ openecomp-facade-api openecomp-facade-api - org.openecomp.core - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-core/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-core/pom.xml index 0c5d36abcc..f59a136e6f 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-core/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/openecomp-facade-core/pom.xml @@ -11,21 +11,21 @@ openecomp-facade-core openecomp-facade-core - org.openecomp.core + - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-facade-api ${project.version} diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/pom.xml index 29e9cce688..18bc9c5c9a 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-facade-lib/pom.xml @@ -4,7 +4,7 @@ openecomp-core-lib - org.openecomp.core + org.openecomp.sdc 1.0.0-SNAPSHOT @@ -19,13 +19,13 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-api ${project.version} compile - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} runtime diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-api/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-api/pom.xml index 174f2a36d9..c169e15d54 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-api/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-api/pom.xml @@ -11,11 +11,11 @@ openecomp-nosqldb-api openecomp-nosqldb-api - org.openecomp.core + - org.openecomp.core + org.openecomp.sdc openecomp-facade-api ${project.version} @@ -30,7 +30,7 @@ ${datasatx.version} - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-core/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-core/pom.xml index d799edb961..32c5388c25 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-core/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/openecomp-nosqldb-core/pom.xml @@ -9,14 +9,13 @@ ../../.. - org.openecomp.core openecomp-nosqldb-core openecomp-nosqldb-core - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-api ${project.version} diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/pom.xml index cb1d810547..d514a73b96 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-nosqldb-lib/pom.xml @@ -10,7 +10,7 @@ openecomp-nosqldb-lib - org.openecomp.core + openecomp-nosqldb-lib pom @@ -21,13 +21,13 @@ - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-api ${project.version} compile - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} runtime diff --git a/openecomp-be/lib/openecomp-core-lib/openecomp-utilities-lib/pom.xml b/openecomp-be/lib/openecomp-core-lib/openecomp-utilities-lib/pom.xml index a7a53a32f0..c22df85a87 100644 --- a/openecomp-be/lib/openecomp-core-lib/openecomp-utilities-lib/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/openecomp-utilities-lib/pom.xml @@ -4,7 +4,7 @@ openecomp-core-lib - org.openecomp.core + org.openecomp.sdc 1.0.0-SNAPSHOT diff --git a/openecomp-be/lib/openecomp-core-lib/pom.xml b/openecomp-be/lib/openecomp-core-lib/pom.xml index 312b085424..16a8e8d0bf 100644 --- a/openecomp-be/lib/openecomp-core-lib/pom.xml +++ b/openecomp-be/lib/openecomp-core-lib/pom.xml @@ -8,7 +8,7 @@ 1.0.0-SNAPSHOT - org.openecomp.core + openecomp-core-lib openecomp-core-lib diff --git a/openecomp-be/lib/openecomp-heat-lib/pom.xml b/openecomp-be/lib/openecomp-heat-lib/pom.xml index 47c7c728a0..046f1456cd 100644 --- a/openecomp-be/lib/openecomp-heat-lib/pom.xml +++ b/openecomp-be/lib/openecomp-heat-lib/pom.xml @@ -10,7 +10,7 @@ openecomp-heat-lib - org.openecomp.core + openecomp-heat-lib @@ -21,7 +21,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -31,7 +31,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-action-lib/openecomp-sdc-action-api/pom.xml b/openecomp-be/lib/openecomp-sdc-action-lib/openecomp-sdc-action-api/pom.xml index d0512a480d..feee6ae16c 100644 --- a/openecomp-be/lib/openecomp-sdc-action-lib/openecomp-sdc-action-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-action-lib/openecomp-sdc-action-api/pom.xml @@ -15,18 +15,18 @@ - org.openecomp.core + org.openecomp.sdc openecomp-core-lib pom ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-api/pom.xml b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-api/pom.xml index a2038a212e..982938f570 100644 --- a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-api/pom.xml @@ -17,7 +17,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-core/pom.xml b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-core/pom.xml index 1f0452cdfb..1c6a6ddda6 100644 --- a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-core/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-core/pom.xml @@ -49,7 +49,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/pom.xml b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/pom.xml index 8f82967e74..a15e6399e9 100644 --- a/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-enrichment-lib/openecomp-sdc-enrichment-impl/pom.xml @@ -48,7 +48,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -63,7 +63,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} @@ -80,12 +80,12 @@ - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-tosca-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-api/pom.xml b/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-api/pom.xml index 81016a413b..e0a03b7551 100644 --- a/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-api/pom.xml @@ -14,7 +14,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} @@ -34,12 +34,12 @@ 1.5.3 - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-tosca-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-core/pom.xml b/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-core/pom.xml index ba4d404ae7..bde2b9987d 100644 --- a/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-core/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-core/pom.xml @@ -28,7 +28,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-impl/pom.xml b/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-impl/pom.xml index ff376f1e7d..f1471d9699 100644 --- a/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-impl/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-model-lib/openecomp-sdc-model-impl/pom.xml @@ -28,7 +28,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-api/pom.xml b/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-api/pom.xml index e6eca41134..b6a59750a0 100644 --- a/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-api/pom.xml @@ -14,7 +14,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} @@ -29,12 +29,12 @@ 1.5.3 - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-tosca-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-core/pom.xml b/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-core/pom.xml index 2bfa7dfd31..64553653fc 100644 --- a/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-core/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-translator-lib/openecomp-sdc-translator-core/pom.xml @@ -45,7 +45,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -81,7 +81,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-api/pom.xml b/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-api/pom.xml index 4f92b644c4..052c19efb0 100644 --- a/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-api/pom.xml @@ -14,7 +14,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-core/pom.xml b/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-core/pom.xml index 9a68e2e11e..b683d51507 100644 --- a/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-core/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-core/pom.xml @@ -58,7 +58,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-impl/pom.xml b/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-impl/pom.xml index bacd79a97a..5d1dc4ffa5 100644 --- a/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-impl/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-validation-lib/openecomp-sdc-validation-impl/pom.xml @@ -57,7 +57,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -67,7 +67,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} @@ -84,7 +84,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-heat-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-vendor-license-lib/openecomp-sdc-vendor-license-api/pom.xml b/openecomp-be/lib/openecomp-sdc-vendor-license-lib/openecomp-sdc-vendor-license-api/pom.xml index 1cb542eba5..817b89823f 100644 --- a/openecomp-be/lib/openecomp-sdc-vendor-license-lib/openecomp-sdc-vendor-license-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-vendor-license-lib/openecomp-sdc-vendor-license-api/pom.xml @@ -14,7 +14,7 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} @@ -24,7 +24,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-api/pom.xml b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-api/pom.xml index 100e85536d..a8019cf30c 100644 --- a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-api/pom.xml @@ -20,18 +20,18 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-core-lib pom ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/pom.xml b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/pom.xml index c732472152..c07b135b04 100644 --- a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/pom.xml @@ -36,7 +36,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-api ${project.version} @@ -52,7 +52,7 @@ test - org.openecomp.core + org.openecomp.sdc openecomp-config-lib ${project.version} diff --git a/openecomp-be/lib/openecomp-sdc-versioning-lib/openecomp-sdc-versioning-api/pom.xml b/openecomp-be/lib/openecomp-sdc-versioning-lib/openecomp-sdc-versioning-api/pom.xml index 180410dcf0..9c783c179f 100644 --- a/openecomp-be/lib/openecomp-sdc-versioning-lib/openecomp-sdc-versioning-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-versioning-lib/openecomp-sdc-versioning-api/pom.xml @@ -14,17 +14,17 @@ - org.openecomp.core + org.openecomp.sdc openecomp-facade-core ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-facade-api ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-nosqldb-core ${project.version} diff --git a/openecomp-be/lib/openecomp-tosca-lib/pom.xml b/openecomp-be/lib/openecomp-tosca-lib/pom.xml index eb67349896..66c9ef5639 100644 --- a/openecomp-be/lib/openecomp-tosca-lib/pom.xml +++ b/openecomp-be/lib/openecomp-tosca-lib/pom.xml @@ -10,14 +10,14 @@ 1.0.0-SNAPSHOT - org.openecomp.core + openecomp-tosca-lib openecomp-tosca-lib - org.openecomp.core + org.openecomp.sdc openecomp-utilities-lib ${project.version} @@ -27,7 +27,7 @@ ${project.version} - org.openecomp.core + org.openecomp.sdc openecomp-common-lib ${project.version} diff --git a/openecomp-be/tools/swagger-ui/api-docs/api.html b/openecomp-be/tools/swagger-ui/api-docs/api.html index 35bf7db136..f62de6767c 100644 --- a/openecomp-be/tools/swagger-ui/api-docs/api.html +++ b/openecomp-be/tools/swagger-ui/api-docs/api.html @@ -9572,11 +9572,11 @@ Performs one of the following actions on a action: |Checkout: Locks it for edits - contentDisposition + contentId -
ContentDisposition + string optional @@ -9585,10 +9585,10 @@ Performs one of the following actions on a action: |Checkout: Locks it for edits - dataHandler + contentDisposition - DataHandler + ContentDisposition @@ -9598,11 +9598,11 @@ Performs one of the following actions on a action: |Checkout: Locks it for edits - contentId + dataHandler + DataHandler - string optional @@ -12054,7 +12054,7 @@ Performs one of the following actions on a action: |Checkout: Locks it for edits - wildcardSubtype + wildcardType @@ -12067,7 +12067,7 @@ Performs one of the following actions on a action: |Checkout: Locks it for edits - wildcardType + wildcardSubtype diff --git a/openecomp-be/tools/swagger-ui/api-docs/api.json b/openecomp-be/tools/swagger-ui/api-docs/api.json index 8396339628..efcb5e84ab 100644 --- a/openecomp-be/tools/swagger-ui/api-docs/api.json +++ b/openecomp-be/tools/swagger-ui/api-docs/api.json @@ -8,35 +8,35 @@ }, "basePath" : "/onboarding-api", "tags" : [ { - "name" : "Vendor License Models" + "name" : "Vendor Software Product Component NICs" }, { - "name" : "Vendor License Model - Entitlement Pools" + "name" : "Vendor Software Product Component Processes" }, { - "name" : "Vendor Software Product Processes" + "name" : "Vendor Software Product Component MIB Uploads" + }, { + "name" : "Vendor License Model - License Key Groups" }, { "name" : "Application Configuration" }, { - "name" : "Actions" + "name" : "Vendor License Model - License Agreements" }, { - "name" : "Vendor License Model - License Key Groups" + "name" : "Vendor License Model - Feature Groups" }, { - "name" : "Vendor Software Product Component Processes" + "name" : "Vendor Software Product Processes" }, { - "name" : "Vendor Software Product Components" + "name" : "Actions" }, { "name" : "Validation" }, { - "name" : "Vendor Software Product Component MIB Uploads" + "name" : "Vendor Software Product Components" }, { - "name" : "Vendor License Model - License Agreements" + "name" : "Vendor Software Products" }, { "name" : "Vendor Software Product Networks" }, { - "name" : "Vendor Software Products" - }, { - "name" : "Vendor Software Product Component NICs" + "name" : "Vendor License Model - Entitlement Pools" }, { - "name" : "Vendor License Model - Feature Groups" + "name" : "Vendor License Models" } ], "schemes" : [ "http" ], "paths" : { @@ -3513,14 +3513,14 @@ "contentType" : { "$ref" : "#/definitions/MediaType" }, + "contentId" : { + "type" : "string" + }, "contentDisposition" : { "$ref" : "#/definitions/ContentDisposition" }, "dataHandler" : { "$ref" : "#/definitions/DataHandler" - }, - "contentId" : { - "type" : "string" } } }, @@ -4343,11 +4343,11 @@ "type" : "string" } }, - "wildcardSubtype" : { + "wildcardType" : { "type" : "boolean", "default" : false }, - "wildcardType" : { + "wildcardSubtype" : { "type" : "boolean", "default" : false } diff --git a/openecomp-ui/.babelrc b/openecomp-ui/.babelrc new file mode 100644 index 0000000000..635081d464 --- /dev/null +++ b/openecomp-ui/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": ["stage-0", "react"], + "plugins": [ + "transform-es2015-modules-commonjs", + "transform-es2015-destructuring", + "transform-es2015-spread", + "transform-object-rest-spread", + "transform-class-properties" + ] +} diff --git a/openecomp-ui/.editorconfig b/openecomp-ui/.editorconfig new file mode 100644 index 0000000000..fcdada6db0 --- /dev/null +++ b/openecomp-ui/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# tab indentation +[src/**.js] +[src/**.jsx] +indent_style = tab + + diff --git a/openecomp-ui/.eslintignore b/openecomp-ui/.eslintignore new file mode 100644 index 0000000000..bd11f6ec26 --- /dev/null +++ b/openecomp-ui/.eslintignore @@ -0,0 +1,4 @@ +webpack.config*.js +gulpfile.js +fixture +tools diff --git a/openecomp-ui/.eslintrc b/openecomp-ui/.eslintrc new file mode 100644 index 0000000000..da8a422df3 --- /dev/null +++ b/openecomp-ui/.eslintrc @@ -0,0 +1,155 @@ +{ + "parser": "babel-eslint", + "env": { + "es6": true, + "jquery": true, + "node": true, + "mocha": true + }, + "plugins": [ + "react", + "import" + ], + "ecmaFeatures": { + "jsx": true, + "classes": true, + "modules": true + }, + "globals": { + "Event": true, + "window": true, + "navigator": true, + "System": true, + "document": true, + "localStorage": true, + "sessionStorage": true, + "Image": true, + "requestAnimationFrame": true, + "cancelAnimationFrame": true, + "DEBUG": true, + "SVGElement": true, + "FormData": true, + "DEV": true, + "Blob": true, + "XMLHttpRequest": true, + "URL": true, + "PunchOutRegistry": true, + "it": true, + "describe": true + }, + "rules": { + "linebreak-style": 0, + "no-unused-vars": 2, + "no-bitwise": 0, + "no-eq-null": 2, + "eqeqeq": 2, + "wrap-iife": [ + 2, + "any" + ], + "no-unused-expressions": 2, + "indent": [ + 1, + "tab", + { + "SwitchCase": 1 + } + ], + "no-use-before-define": 2, + "new-cap": [ + 2, + { + "capIsNewExceptions": [ + "DataTable", + "V" + ] + } + ], + "no-caller": 2, + "no-empty": 2, + "no-undef": 2, + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "jsx-quotes": [ + 2, + "prefer-single" + ], + "no-plusplus": 0, + "no-cond-assign": [ + 2, + "except-parens" + ], + "comma-style": [ + 2, + "last" + ], + "no-invalid-this": 0, + "dot-notation": 0, + "max-len": [ + 1, + 200 + ], + "camelcase": [ + 2, + { + "properties": "never" + } + ], + "curly": 2, + "brace-style": 0, + "semi": [ + 2, + "always" + ], + "space-in-brackets": [ + 0, + "never" + ], + "space-infix-ops": 2, + "import/default": 0, + "import/no-unresolved": 0, + "import/no-named-as-default": 2, + "import/no-duplicates": 0, + "import/imports-first": 2, + "import/export": 2, + "react/display-name": 0, + "react/forbid-prop-types": 0, + "react/jsx-boolean-value": 0, + "react/jsx-closing-bracket-location": [ + 1, + { + "nonEmpty": "after-props", + "selfClosing": "after-props" + } + ], + "react/jsx-curly-spacing": 0, + "react/jsx-indent-props": [ + 1, + "tab" + ], + "react/jsx-max-props-per-line": 0, + "react/jsx-no-duplicate-props": 1, + "react/jsx-no-literals": 0, + "react/jsx-no-undef": 1, + "react/jsx-sort-prop-types": 0, + "react/jsx-sort-props": 0, + "react/jsx-uses-react": 1, + "react/jsx-uses-vars": 1, + "react/no-danger": 1, + "react/no-did-mount-set-state": 2, + "react/no-did-update-set-state": 2, + "react/no-direct-mutation-state": 1, + "react/no-multi-comp": 0, + "react/no-set-state": 0, + "react/no-unknown-property": 1, + "react/prop-types": 0, + "react/react-in-jsx-scope": 1, + "react/require-extension": 1, + "react/self-closing-comp": 1, + "react/sort-comp": 0, + "react/wrap-multilines": 1 + } +} diff --git a/openecomp-ui/.gitignore b/openecomp-ui/.gitignore new file mode 100644 index 0000000000..cce0a545f7 --- /dev/null +++ b/openecomp-ui/.gitignore @@ -0,0 +1,7 @@ + +.idea + +dist +node_modules +devConfig.json +.npmrc diff --git a/openecomp-ui/LICENSE b/openecomp-ui/LICENSE new file mode 100644 index 0000000000..2507db8a57 --- /dev/null +++ b/openecomp-ui/LICENSE @@ -0,0 +1 @@ +(c) Copyright 2016 ECOMP, all rights reserved. diff --git a/openecomp-ui/devConfig.defaults.json b/openecomp-ui/devConfig.defaults.json new file mode 100644 index 0000000000..bfcf9aae8f --- /dev/null +++ b/openecomp-ui/devConfig.defaults.json @@ -0,0 +1,6 @@ +{ + "port": 9000, + "proxyATTTarget": null, + "proxyTarget": null, + "useFixture": false +} diff --git a/openecomp-ui/fixture/data/entitlementPools.json b/openecomp-ui/fixture/data/entitlementPools.json new file mode 100644 index 0000000000..22750cbb6d --- /dev/null +++ b/openecomp-ui/fixture/data/entitlementPools.json @@ -0,0 +1,36 @@ +{ + "results":[ + { + "name": "ep1", + "description": "string", + "thresholdValue": 75, + "thresholdUnits": "%", + "entitlementMetric": {"choice": "User", "other":""}, + "increments": "string", + "aggregationFunction": {"choice": "Average", "other": ""}, + "operationalScope": {"choices": ["VM"], "other": ""}, + "time": {"choice": "Hour", "other": ""}, + "sku": "DEF2-385A-4521-AAAA", + "id": "1", + "referencingFeatureGroups": ["1","2"], + "partNumber": "51529" + }, + { + "name": "ep2", + "description": "string", + "thresholdValue": 99, + "thresholdUnits": "%", + "entitlementMetric": {"choice": "User", "other":""}, + "increments": "string", + "aggregationFunction": {"choice": "Average", "other": ""}, + "operationalScope": {"choices": ["Other"], "other": "blabla"}, + "time": {"choice": "Hour", "other": ""}, + "sku": "DEF2-385A-4521-AAAA", + "id": "2", + "refCount": 0, + "partNumber": "51529", + "referencingFeatureGroups": [] + } + ] +} + diff --git a/openecomp-ui/fixture/data/featureGroup.json b/openecomp-ui/fixture/data/featureGroup.json new file mode 100644 index 0000000000..278baecb36 --- /dev/null +++ b/openecomp-ui/fixture/data/featureGroup.json @@ -0,0 +1,59 @@ +{ + "name": "fssss", + "description": "gdfgdfgdsfg", + "id": "1", + "licenseKeyGroupsIds": [ + "1,2" + ], + "entitlementPoolsIds": [ + "1,2" + ], + "licenseKeyGroups": [ + { + "name": "ls1", + "description": "string", + "type": "string", + "operationalScope": "string", + "id": "1", + "refCount": 0 + }, + { + "name": "ls2", + "description": "string", + "type": "string", + "operationalScope": "string", + "id": "1", + "refCount": 0 + } + ], + "entitlementPools": [ + { + "name": "ep1", + "description": "string", + "thresholdValue": 0, + "thresholdUnits": "string", + "entitlementMetric": "string", + "increments": "string", + "aggregationFunction": "string", + "operationalScope": "string", + "time": "string", + "sku": "string", + "id": "string", + "refCount": 0 + }, + { + "name": "ep2", + "description": "string", + "thresholdValue": 0, + "thresholdUnits": "string", + "entitlementMetric": "string", + "increments": "string", + "aggregationFunction": "string", + "operationalScope": "string", + "time": "string", + "sku": "string", + "id": "string", + "refCount": 0 + } + ] +} diff --git a/openecomp-ui/fixture/data/featureGroups.json b/openecomp-ui/fixture/data/featureGroups.json new file mode 100644 index 0000000000..eea4967ca8 --- /dev/null +++ b/openecomp-ui/fixture/data/featureGroups.json @@ -0,0 +1,28 @@ +{ + "results": [ + { + "name": "fs1", + "id": "0", + "description": "fs1-description", + "licenseKeyGroupsIds": [ + "1" + ], + "entitlementPoolsIds": [ + "1" + ], + "referencingLicenseAgreements": ["1","2"] + }, + { + "name": "fs2", + "id": "1", + "description": "fs2-description", + "licenseKeyGroupsIds": [ + "2" + ], + "entitlementPoolsIds": [ + "2" + ], + "referencingLicenseAgreements": [] + } + ] +} diff --git a/openecomp-ui/fixture/data/licenseAgreementList.json b/openecomp-ui/fixture/data/licenseAgreementList.json new file mode 100644 index 0000000000..b113295fcb --- /dev/null +++ b/openecomp-ui/fixture/data/licenseAgreementList.json @@ -0,0 +1,33 @@ +{ + "results": [ + { + "name": "name0", + "description": "description0", + "licenseTerm": {"choice": "Other", "other": "blabla"}, + "requirementsAndConstrains": "requirementsAndConstrains0", + "featureGroupsIds": [], + "id": "0" + }, + { + "name": "name1", + "description": "description1", + "licenseTerm": {"choice": "Fixed_Term", "other": ""}, + "requirementsAndConstrains": "requirementsAndConstrains1", + "featureGroupsIds": [ + "1" + ], + "id": "1" + }, + { + "name": "name2", + "description": "description2", + "licenseTerm": {"choice": "Unlimited", "other": ""}, + "requirementsAndConstrains": "requirementsAndConstrains2", + "featureGroupsIds": [ + "2" + ], + "id": "2" + } + ], + "listCount": 3 +} diff --git a/openecomp-ui/fixture/data/licenseKeyGroups.json b/openecomp-ui/fixture/data/licenseKeyGroups.json new file mode 100644 index 0000000000..74050ab033 --- /dev/null +++ b/openecomp-ui/fixture/data/licenseKeyGroups.json @@ -0,0 +1,20 @@ +{ + "results":[ + { + "name": "lsk1", + "description": "string", + "type": "Unique", + "operationalScope": {"choices": ["Network_Wide","VM"], "other": ""}, + "id": "1", + "referencingFeatureGroups":["1","2"] + }, + { + "name": "lsk2", + "description": "string", + "type": "One_Time", + "operationalScope": {"choices": ["Other"], "other": "blabla"}, + "id": "2", + "referencingFeatureGroups": 0 + } + ] +} diff --git a/openecomp-ui/fixture/data/licenseModels.json b/openecomp-ui/fixture/data/licenseModels.json new file mode 100644 index 0000000000..5239c4fafc --- /dev/null +++ b/openecomp-ui/fixture/data/licenseModels.json @@ -0,0 +1,16 @@ +{ + "results":[ + { + "vendorName": "Omer Corp.", + "description": "", + "iconRef": "string", + "id": "1" + }, + { + "vendorName": "Robotricks", + "description": "Optimus Prime", + "iconRef": "string", + "id": "2" + } + ] +} diff --git a/openecomp-ui/fixture/data/softwareProduct.json b/openecomp-ui/fixture/data/softwareProduct.json new file mode 100644 index 0000000000..5b60587614 --- /dev/null +++ b/openecomp-ui/fixture/data/softwareProduct.json @@ -0,0 +1,96 @@ +{ + "name": "VSP5", + "version": "0.1", + "id": "4730033D16C64E3CA556AB0AC4478218", + "description": "A software model for Fortigate. Nam hendrerit sollicitudin semper. Aenean consectetur nisi sit amet ante sodales consectetur. Nullam rutrum massa in pellentesque ' +elementum. Aliquam efficitur tellus lacus, eget iaculis justo iaculis eu. ", + "categoryId": "category", + "vendorId": "1", + "checkinStatus": "CHECK_OUT", + "licensingData": "test data", + "validationData": { + "logicalStructure": [ + { + "type": "PPD", + "catalogInstances": [ + { + "name": "PPD1", + "artifacts": [ + { + "name": "chopstick.py" + }, + { + "name": "bread.py" + } + ] + } + ] + } + ], + "importStructure": { + "HEAT": [ + { + "fileName": "sushi.yml", + "env": "soy.env", + "nested": [ + { + "fileName": "salmon.yml", + "env": "skin.env", + "artifacts": [ + { + "name": "rice.py", + "status": "OK" + }, + { + "name": "tuna.py", + "status": "Missing" + } + ] + } + ], + "artifacts": [ + { + "name": "chopstick.py", + "status": "OK" + }, + { + "name": "bread.py", + "status": "Missing" + } + ], + "volume": [ + { + "fileName": "fishtank.yml", + "env": "middletown.env" + } + ], + "network": [ + { + "fileName": "fishnet.yml", + "env": "ship.env" + } + ] + } + ], + "volume": [ + { + "fileName": "vol1.yml", + "env": "e1.env" + }, + { + "fileName": "vol2.yml", + "env": "e2.env" + } + ], + "network": [ + { + "fileName": "net1.yml", + "env": "env1.env" + }, + { + "fileName": "net2.yml", + "env": "env2.env" + } + ] + } + } +} diff --git a/openecomp-ui/fixture/data/softwareProductList.json b/openecomp-ui/fixture/data/softwareProductList.json new file mode 100644 index 0000000000..4554abc95f --- /dev/null +++ b/openecomp-ui/fixture/data/softwareProductList.json @@ -0,0 +1,34 @@ +{ + "results": [ + { + "name": "Software Product 1", + "version": "1.0", + "id": "1", + "category": "Category1", + "subCategory": "Sub Category1", + "vendor": "1", + "status": "string", + "checkinStatus": "string" + }, + { + "name": "Software Product 2", + "version": "1.0", + "id": "2", + "category": "Category2", + "subCategory": "Sub Category2", + "vendor": "2", + "status": "string", + "checkinStatus": "string" + }, + { + "name": "Software Product 3", + "version": "1.0", + "id": "3", + "category": "Category3", + "subCategory": "Sub Category3", + "vendor": "3", + "status": "string", + "checkinStatus": "string" + } + ] +} diff --git a/openecomp-ui/fixture/express.js b/openecomp-ui/fixture/express.js new file mode 100644 index 0000000000..ed8bf956f9 --- /dev/null +++ b/openecomp-ui/fixture/express.js @@ -0,0 +1,231 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var args = process.argv.slice(2); + +function defineRoutes(router) { + + //LICENSE-MODELS + router.get('/v1.0/vendor-license-models', licenseModelsList); + + //FEATURE-GROUP + router.get('/v1.0/vendor-license-models/:licenseModelId/feature-groups', featureGroupList); + router.get('/v1.0/vendor-license-models/:licenseModelId/feature-groups/:featureGroupId', featureGroup); + router.post('/v1.0/vendor-license-models/:licenseModelId/feature-groups', addFeatureGroup); + router.delete('/v1.0/vendor-license-models/:licenseModelId/feature-groups/:featureGroupId', deletefeatureGroup); + router.put('/v1.0/vendor-license-models/:licenseModelId/feature-groups/:featureGroupId', updatefeatureGroup); + + + + //LICENSE-AGREEMENT + router.get('/v1.0/vendor-license-models/:licenseModelId/license-agreements', licenseAgreementList); + router.post('/v1.0/vendor-license-models/:licenseModelId/license-agreements/', addLicenseAgreement); + router.delete('/v1.0/vendor-license-models/:licenseModelId/license-agreements/:licenseAgreementId', deleteLicenseAgreement); + router.put('/v1.0/vendor-license-models/:licenseModelId/license-agreements/:licenseAgreementId', updateLicenseAgreement); + + //ENTITLEMENT POOLS + router.get('/v1.0/vendor-license-models/:licenseModelId/entitlement-pools', entitlementPoolsList); + router.post('/v1.0/vendor-license-models/:licenseModelId/entitlement-pools', addEntitlementPool); + router.put('/v1.0/vendor-license-models/:licenseModelId/entitlement-pools/:entitlementPoolId', updateEntitlementPool); + router.delete('/v1.0/vendor-license-models/:licenseModelId/entitlement-pools/:entitlementPoolId', deleteEntitlementPool); + + //LICENSE KEY GROUPS + router.get('/v1.0/vendor-license-models/:licenseModelId/license-key-groups', licenseKeyGroupsList); + router.post('/v1.0/vendor-license-models/:licenseModelId/license-key-groups', addLicenseKeyGroup); + router.delete('/v1.0/vendor-license-models/:licenseModelId/license-key-groups/:licenseKeyGroupId', deleteLicenseKeyGroup); + router.put('/v1.0/vendor-license-models/:licenseModelId/license-key-groups/:licenseKeyGroupId', updateLicenseKeyGroup); + + //VENDOR SOFTWARE PRODUCT + + router.post('/v1.0/vendor-software-products/:vspId/upload', softwareProductUpload); + router.get('/v1.0/vendor-software-products/:vspId', getSoftwareProduct); + router.get('/v1.0/vendor-software-products', softwareProductList); + + router.put('/v1.0/vendor-software-products/:vspId/processes/:prcId', putSoftwareProductProcess); + router.post('/v1.0/vendor-software-products/:vspId/processes', postSoftwareProductProcess); +} + + +function licenseModelsList(req, res) { + res.json(require('./data/licenseModels')); +} + +function featureGroupList(req, res) { + res.json(require('./data/featureGroups')); +} + +function featureGroup(req, res) { + res.json(require('./data/featureGroup')); +} + +function deletefeatureGroup(req, res) { + res.json({ + returnCode: 'OK' + }); +} + + +function updatefeatureGroup(req, res) { + res.json({ + returnCode: 'OK' + }); +} + +function addFeatureGroup(req,res) { + var id = Math.floor(Math.random() * (100 - 1) + 1).toString(); + res.json({ + returnCode: 'OK', + value: id + }) +} + +/** ENTITLEMENT POOLS **/ +function entitlementPoolsList(req, res) { + res.json(require('./data/entitlementPools')); +} + +function updateEntitlementPool(req, res) { + res.json({ + returnCode: 'OK' + }); +} + +function addEntitlementPool(req,res) { + var id = Math.floor(Math.random() * (100 - 1) + 1).toString(); + res.json({ + returnCode: 'OK', + value: id + }) +} + +function deleteEntitlementPool(req, res) { + res.json({ + returnCode: 'OK' + }); +} + +/** LICENSE KEY GROUPS */ + +function licenseKeyGroupsList(req, res) { + res.json(require('./data/licenseKeyGroups')); +} + +function addLicenseKeyGroup(req,res) { + var id = Math.floor(Math.random() * (100 - 1) + 1).toString(); + res.json({ + returnCode: 'OK', + value: id + }) +} + +function deleteLicenseKeyGroup(req, res) { + res.json({ + returnCode: 'OK' + }); +} + +function updateLicenseKeyGroup(req, res) { + res.json({ + returnCode: 'OK' + }); +} + +function licenseAgreementList(req, res) { + res.json(require('./data/licenseAgreementList')); +} + + +function addLicenseAgreement(req,res) { + var id = Math.floor(Math.random() * (100 - 1) + 1).toString(); + res.json({ + returnCode: 'OK', + value: id + }) +} +function deleteLicenseAgreement(req, res) { + res.json({ + returnCode: 'OK' + }); +} +function updateLicenseAgreement(req, res) { + res.json({ + returnCode: 'OK' + }); +} + +/** VENDOR SOFTWARE PRODUCT */ + +function softwareProductUpload(req, res) { + res.json({ + status: 'SUCCESS' + }); +} + +function getSoftwareProduct(req, res) { + res.json(require('./data/softwareProduct')); +} + + +function putSoftwareProductProcess(req, res) { + res.json({ + status: 'SUCCESS' + }); +} + +function postSoftwareProductProcess(req, res) { + var id = Math.floor(Math.random() * (100 - 1) + 1).toString(); + res.json({ + returnCode: 'OK', + value: id + }); +} + + + + +function createFixtureServer(port) { + var express = require('express'); + var app = express(); + var bodyParser = require('body-parser'); + app.use(bodyParser.urlencoded({extended: true})); + app.use(bodyParser.json()); + + var router = express.Router(); + + defineRoutes(router); + + app.use('/api', router); + app.use('/onboarding-api', router); + app.use('/sdc1/feProxy/onboarding-api', router); + + app.listen(port); + + console.log('Fixture server is up. port->', port); + //console.log(router.stack); + return app; +} + +/** SOFTWARE PRODUCT LIST **/ +function softwareProductList(req, res) { + res.json(require('./data/softwareProductList')); +} + + +createFixtureServer(args[0]); diff --git a/openecomp-ui/fixture/fixture.js b/openecomp-ui/fixture/fixture.js new file mode 100644 index 0000000000..7e5b263c5c --- /dev/null +++ b/openecomp-ui/fixture/fixture.js @@ -0,0 +1,93 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +'use strict'; + +const PORT = 4000; + +function startFixtureServer() { + return require('child_process').fork('fixture/express', [PORT]); +} + +function buildProxyMiddleware() { + return require('./middleware')(PORT); +} + +function getProxyData(fixtureServerOptions, req, res, next) { + if (!fixtureServerOptions.serverProcess) { + fixtureServerOptions.serverProcess = startFixtureServer(); + } + + return fixtureServerOptions.proxy(req, res, next); +} + +function wrapFixture(fixtureServerOptions, req, res, next) { + if (fixtureServerOptions.proxy) { + return getProxyData(fixtureServerOptions, req, res, next); + } else { + next(); + } +} + + +module.exports = function fixture(options) { + + let proxy; + if(options.enabled) { + proxy = buildProxyMiddleware(); + } + + let fixtureServerOptions = { + proxy, + serverProcess: null + }; + + (function startWatch() { + var nodeWatch = require('node-watch'); + + nodeWatch(['fixture/data', 'fixture/express.js'], function () { + if (fixtureServerOptions.proxy && fixtureServerOptions.serverProcess) { + fixtureServerOptions.serverProcess.kill(); + fixtureServerOptions.serverProcess = startFixtureServer(); + } + }); + + nodeWatch(['devConfig.json'], function () { + let devConfigDefaults = require('../devConfig.defaults'); + require('fs').readFile('devConfig.json', (err, data) => { + if (err) throw err; + const config = Object.assign({}, devConfigDefaults, JSON.parse(data)); + if (config.useFixture) { + fixtureServerOptions.proxy = proxy || buildProxyMiddleware(); + } + else { + fixtureServerOptions.proxy = null; + if (fixtureServerOptions.serverProcess) { + fixtureServerOptions.serverProcess.kill(); + fixtureServerOptions.serverProcess = null; + } + } + }); + }); + + })(); + + return (req, res, next) => wrapFixture(fixtureServerOptions, req, res, next); +}; diff --git a/openecomp-ui/fixture/middleware.js b/openecomp-ui/fixture/middleware.js new file mode 100644 index 0000000000..915bb81cab --- /dev/null +++ b/openecomp-ui/fixture/middleware.js @@ -0,0 +1,24 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +module.exports = function createProxyMiddleware(port) { + var proxy = require('http-proxy-middleware'); + return proxy(['/api', '/onboarding-api'], {target: 'http://localhost:' + port}); +}; diff --git a/openecomp-ui/gulpfile.js b/openecomp-ui/gulpfile.js new file mode 100644 index 0000000000..57885ec6ca --- /dev/null +++ b/openecomp-ui/gulpfile.js @@ -0,0 +1,292 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var gulpHelpers = require('gulp-helpers'); +var taskMaker = gulpHelpers.taskMaker(gulp); +var _ = gulpHelpers.framework('_'); +var runSequence = gulpHelpers.framework('run-sequence'); +var i18nTask = require('./tools/gulp/tasks/i18n'); +var prodTask = require('./tools/gulp/tasks/prod'); +var gulpCssUsage = require('gulp-css-usage').default; +var webpack = require('webpack'); +var WebpackDevServer = require('webpack-dev-server'); + +var localDevConfig = {}; +try { + localDevConfig = require('./devConfig'); +} catch (e) { +} +var devConfig = Object.assign({}, require('./devConfig.defaults'), localDevConfig); +var webpackConfig = require('./webpack.config'); + +function defineTasks(mode) { + let appName = 'onboarding'; + let dist = 'dist/' + mode + '/'; + + let path = { + locales: 'i18n/', + jssource: 'src/**/*.js', + jsxsource: 'src/**/*.jsx', + html: '**/*.html', + output: dist, + assets: './resources/**/*.{css,png,svg,eot,ttf,woff,woff2,otf}', + json: './src/**/*.json', + index: './src/index.html', + heat: './src/heat.html', + watch: ['./src/**'], + scss: './resources/scss/**/*.scss', + css: dist + '/css', + war: [dist + 'index.html', dist + 'punch-outs_en.js', dist + '**/*.{css,png,svg,eot,ttf,woff,woff2,otf}', dist + '**/*(config.json|locale.json)', 'tools/gulp/deployment/**', 'webapp-onboarding/**'], + heatWar: [dist + 'heat.html', dist + 'heat-validation_en.js', dist + '**/*.{css,png,svg,eot,ttf,woff,woff2,otf}', dist + '**/*(config.json|locale.json)', 'webapp-heat-validation/**'], + wardest: 'dist/' + }; + + taskMaker.defineTask('clean', {taskName: 'clean', src: path.output}); + taskMaker.defineTask('copy', {taskName: 'copy-assets', src: path.assets, dest: path.output}); + taskMaker.defineTask('copy', { + taskName: 'copy-json', + src: path.json, + dest: path.output, + changed: {extension: '.json'} + }); + taskMaker.defineTask('copy', { + taskName: 'copy-index.html', + src: path.index, + dest: path.output, + rename: 'index.html' + }); + taskMaker.defineTask('copy', { + taskName: 'copy-heat.html', + src: path.heat, + dest: path.output, + rename: 'heat.html' + }); + taskMaker.defineTask('sass', { + taskName: 'sass', + src: path.scss, + dest: path.css, + config: {outputStyle: 'compressed'} + }); + taskMaker.defineTask('compress', { + taskName: 'compress-war', + src: path.war, + filename: appName + '.war', + dest: path.wardest + }); + taskMaker.defineTask('compress', { + taskName: 'compress-heat-war', + src: path.heatWar, + filename: 'heat-validation.war', + dest: path.wardest + }); + taskMaker.defineTask('watch', { + taskName: 'watch-stuff', + src: [path.assets, path.json, path.index, path.heat], + tasks: ['copy-stuff'] + }); + taskMaker.defineTask('watch', {taskName: 'watch-sass', src: path.scss, tasks: ['sass']}); + + gulp.task('copy-stuff', callback => { + return runSequence(['copy-assets', 'copy-json', 'copy-index.html', 'copy-heat.html'], callback); + }); + + gulp.task('i18n', () => { + return i18nTask({ + outputPath: path.output, + localesPath: path.locales, + lang: 'en' + }).catch(err => { + console.log('i18n Task : Error! ', err); + throw err; + }); + }); + + + gulp.task('dependencies', () => { + //TODO: + }); + +} + +gulp.task('dev', callback => { + defineTasks('dev'); + return runSequence('clean', ['i18n', 'copy-stuff'], 'webpack-dev-server', ['watch-stuff'], callback); +}); + +// Production build +gulp.task('build', callback => { + defineTasks('prod'); + return runSequence('clean', ['copy-stuff', 'i18n'], 'prod', ['compress-war', 'compress-heat-war'], callback); +}); + +gulp.task('default', ['dev']); + +gulp.task('prod', () => { + + // configure webpack for production + let webpackProductionConfig = Object.create(webpackConfig); + + for (let name in webpackProductionConfig.entry) { + webpackProductionConfig.entry[name] = webpackProductionConfig.entry[name].filter(path => !path.startsWith('webpack')); + } + + webpackProductionConfig.cache = true; + webpackProductionConfig.output = { + path: path.join(__dirname, 'dist/prod'), + publicPath: '/onboarding/', + filename: '[name].js' + }; + webpackProductionConfig.resolveLoader = { + root: [path.resolve('.')], + alias: { + 'config-json-loader': 'tools/webpack/config-json-loader/index.js' + } + }; + + // remove source maps + webpackProductionConfig.devtool = undefined; + webpackProductionConfig.module.preLoaders = webpackProductionConfig.module.preLoaders.filter(preLoader => preLoader.loader != 'source-map-loader'); + webpackProductionConfig.module.loaders.forEach(loader => { + if (loader.loaders && loader.loaders[0] === 'style') { + loader.loaders = loader.loaders.map(loaderName => loaderName.replace('?sourceMap', '')); + } + }); + + webpackProductionConfig.module.loaders.push({test: /config.json$/, loaders: ['config-json-loader']}); + webpackProductionConfig.eslint = { + configFile: './.eslintrc', + failOnError: true + }; + webpackProductionConfig.babel = {//TODO: remove this when UglifyJS will support user or + // Webpack 2.0 + presets: ['es2015', 'stage-0', 'react'] + } + webpackProductionConfig.plugins = [ + new webpack.DefinePlugin({ + 'process.env': { + // This has effect on the react lib size + 'NODE_ENV': JSON.stringify('production') + }, + DEBUG: false, + DEV: false + }), + new webpack.optimize.DedupePlugin(), + new webpack.optimize.UglifyJsPlugin() + ]; + + // run production build + return prodTask({ + webpackProductionConfig, + outDir: 'dist/prod' + }) + .then(() => { + }) + .catch(err => { + if (err && err.stack) { + console.error(err, err.stack); + } + throw new Error('Webpack build FAILED'); + }); +}); + +gulp.task('webpack-dev-server', () => { + // modify some webpack config options for development + let myConfig = Object.create(webpackConfig); + + myConfig.devServer.setup = server => { + let fixture = require('./fixture/fixture'); + let proxy = require('http-proxy-middleware'); + 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(; )?/, ''); + } + } + }; + + let middlewares = [ + (req, res, next) => { + 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(); + }, + fixture({ + enabled: devConfig.useFixture + }) + ]; + + // 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' + } + })) + ) + } + + // Ecorp environment (proxyATTTarget) has lower priority, so it should be second + if (devConfig.proxyATTTarget) { + middlewares.push( + proxy(['/sdc1', '/onboarding-api'], 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); + }; + + // Start a webpack-dev-server + let server = new WebpackDevServer(webpack(myConfig), myConfig.devServer); + server.listen(myConfig.devServer.port, '0.0.0.0', err => { + if (err) { + throw new Error('webpack-dev-server' + err); + } + }); +}); + + +gulp.task('gulp-css-usage', callback => { + return gulp.src('src/**/*.jsx').pipe(gulpCssUsage({css: 'dist/dev/css/style.css', babylon: ['objectRestSpread']})); +}); + +gulp.task('css-usage', callback => { + defineTasks('dev'); + runSequence('sass', 'gulp-css-usage'); +}); + diff --git a/openecomp-ui/karma.conf.js b/openecomp-ui/karma.conf.js new file mode 100644 index 0000000000..91c5040942 --- /dev/null +++ b/openecomp-ui/karma.conf.js @@ -0,0 +1,102 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var path = require('path'); +var isparta = require('isparta'); + +module.exports = function (config) { + config.set({ + + browsers: [process.env.JENKINS_HOME ? 'Firefox' : 'Chrome'], + + singleRun: true, + + autoWatchBatchDelay: 50, + + frameworks: ['mocha'], + + files: [ + 'tests.webpack.js' + ], + + preprocessors: { + 'tests.webpack.js': ['webpack', 'sourcemap'], + 'src/**/*.jsx': ['coverage'] + }, + + reporters: ['progress', 'coverage'], + + coverageReporter: { + dir: 'dist/coverage/', + reporters: [ + {type: 'html'}, + {type: 'text-summary'} + ], + includeAllSources: true, + instrumenters: {isparta: isparta}, + instrumenter: { + '**/*.js': 'isparta', + '**/*.jsx': 'isparta' + }, + instrumenterOptions: { + isparta: { + embedSource: true, + noAutoWrap: true, + } + } + }, + + webpack: { + devtool: 'inline-source-map', + resolve: { + root: [path.resolve('.')], + alias: { + i18nJson: 'nfvo-utils/i18n/locale.json', + 'nfvo-utils/RestAPIUtil.js': 'test-utils/MockRest.js', + 'nfvo-utils': 'src/nfvo-utils', + 'nfvo-components': 'src/nfvo-components', + 'sdc-app': 'src/sdc-app' + } + }, + module: { + preLoaders: [ + {test: /\.js$/, exclude: /(src|node_modules)/, loader: 'eslint-loader'}, + {test: /\.(js|jsx)$/, exclude: /(test|test\.js|node_modules)/, loader: 'isparta'} + ], + loaders: [ + {test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader'}, + {test: /\.json$/, loaders: ['json']}, + {test: /\.(css|scss|png|jpg|svg|ttf|eot|otf|woff|woff2)(\?.*)?$/, loader: 'ignore-loader'}, + ] + }, + eslint: { + configFile: './.eslintrc', + emitError: true, + emitWarning: true, + failOnError: true + }, + }, + + webpackServer: { + noInfo: true + } + + }); +}; diff --git a/openecomp-ui/package.json b/openecomp-ui/package.json new file mode 100644 index 0000000000..1d9a479e43 --- /dev/null +++ b/openecomp-ui/package.json @@ -0,0 +1,102 @@ +{ + "name": "dox-ui", + "version": "1.0.0", + "description": "", + "author": "ECOMP", + "license": "SEE LICENSE IN LICENSE", + "scripts": { + "start": "gulp dev", + "build": "gulp build", + "test": "karma start", + "test-dev": "karma start --auto-watch --no-single-run" + }, + "dependencies": { + "classnames": "^2.2.5", + "core-js": "^2.4.0", + "dox-sequence-diagram-ui": "file:../dox-sequence-diagram-ui", + "history": "^1.13.1", + "immutable": "^3.7.5", + "intl": "^1.0.1", + "intl-format-cache": "^2.0.5", + "intl-messageformat": "^1.2.0", + "intl-relativeformat": "^1.2.0", + "jquery": "^2.2.2", + "lodash": "^4.13.1", + "md5": "^2.1.0", + "react": "~15.3.0", + "react-bootstrap": "^0.28.1", + "react-dom": "~15.3.0", + "react-dropzone": "3.4.0", + "react-fontawesome": "^0.3.3", + "react-redux": "^4.4.1", + "react-select": "^1.0.0-beta13", + "redux": "^3.3.1", + "uuid-js": "^0.7.5", + "validator": "^4.3.0" + }, + "devDependencies": { + "babel-core": "^6.9.1", + "babel-eslint": "^6.0.2", + "babel-loader": "^6.2.4", + "babel-plugin-transform-class-properties": "^6.10.2", + "babel-plugin-transform-es2015-destructuring": "^6.9.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.10.3", + "babel-plugin-transform-es2015-spread": "^6.8.0", + "babel-plugin-transform-object-rest-spread": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-react": "^6.5.0", + "babel-preset-stage-0": "^6.5.0", + "chai": "^3.5.0", + "css-loader": "^0.23.1", + "deep-freeze": "0.0.1", + "eslint-loader": "^1.3.0", + "eslint-plugin-import": "^0.8.1", + "eslint-plugin-react": "^3.14.0", + "expect": "^1.20.1", + "express": "^4.13.3", + "file-loader": "^0.8.5", + "gulp": "^3.9.1", + "gulp-clean": "^0.3.1", + "gulp-css-usage": "^2.0.0", + "gulp-helpers": "^5.0.0", + "gulp-jsx-coverage": "^0.3.8", + "gulp-rename": "^1.2.2", + "gulp-replace": "^0.5.4", + "html-loader": "^0.4.3", + "http-proxy-middleware": "^0.8.2", + "ignore-loader": "^0.1.1", + "isparta": "^4.0.0", + "isparta-loader": "^2.0.0", + "istanbul": "^1.0.0-alpha.2", + "istanbul-instrumenter-loader": "^0.2.0", + "jsdom": "^8.3.0", + "json-loader": "^0.5.4", + "jsx-loader": "^0.13.2", + "karma": "^0.13.22", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^1.0.1", + "karma-cli": "^1.0.0", + "karma-coverage": "^1.0.0", + "karma-firefox-launcher": "^1.0.0", + "karma-mocha": "^1.0.1", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^1.7.0", + "mkdirp": "^0.5.1", + "mocha": "^2.4.5", + "node-watch": "^0.3.5", + "prompt": "^0.2.14", + "react-addons-test-utils": "~15.3.0", + "react-hot-loader": "^1.3.0", + "sass-loader": "^3.1.2", + "sinon": "^1.17.3", + "source-map-loader": "^0.1.5", + "style-loader": "^0.13.0", + "url-loader": "^0.5.7", + "webpack": "^1.13.1", + "webpack-dev-server": "^1.14.1" + }, + "engines": { + "node": ">=5.1", + "npm": ">=3.3" + } +} diff --git a/openecomp-ui/pom.xml b/openecomp-ui/pom.xml new file mode 100644 index 0000000000..456469e044 --- /dev/null +++ b/openecomp-ui/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + + + org.openecomp.sdc + sdc-main + 1.0.0-SNAPSHOT + + + org.openecomp.sdc.onboarding + onboarding-fe + onboarding-ui-war + + war + + + + + maven-war-plugin + + webapp-onboarding\WEB-INF\web.xml + + + + + + + + rackspace + + true + + + + rackspace-public + Rackspace + https://10.208.197.75:8443/repository/maven-public/ + default + + + + virtuos + Virtuos + http://nexus.virtuos.uos.de/nexus/content/repositories/public/ + default + + + + apache-public + Apache-Public + https://repository.apache.org/content/groups/public/ + default + + + + elasticsearch-releases + https://maven.elasticsearch.org/releases + + true + + + false + + + + + + + rackspace-snapshots + Rackspace-Snapshots + https://10.208.197.75:8443/repository/maven-snapshots/ + + + + rackspace-public + Rackspace + https://10.208.197.75:8443/repository/maven-releases/ + + + + + + diff --git a/openecomp-ui/readMe.txt b/openecomp-ui/readMe.txt new file mode 100644 index 0000000000..6b70dfc5bb --- /dev/null +++ b/openecomp-ui/readMe.txt @@ -0,0 +1,25 @@ +# OpenECOMP SDC(UI) + +--- +--- + +# Steps + +### Install nodejs & gulp + +#### Download nodejs from here : https://nodejs.org/en/ (take the "current" version with latest features) & install it. +#### Install gulp by running the following command : npm install --global gulp-cli + +### Install DOX-UI a + +#### Pull for latest changes. +#### Go to folder dox-sequence-diagram-ui +#### Give the following command : run npm install +#### Wait for it.. +#### Go to folder dox-ui +#### run npm install +#### Create a copy of devConfig.defaults.json file and name it devConfig.json (we already configured git to ignore it so it will not be pushed). +#### In that file, populate the fields of the IP addresses of your BE machine you'd like to connect (pay attention, it is a JSON file): For example, http://: +#### After everything was successful, run gulp. +#### After server was up, your favourite UI will wait for you at : http://localhost:9000/sdc1/proxy-designer1#/onboardVendor + diff --git a/openecomp-ui/resources/css/font-awesome.min.css b/openecomp-ui/resources/css/font-awesome.min.css new file mode 100644 index 0000000000..24fcc04c4e --- /dev/null +++ b/openecomp-ui/resources/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.3.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.3.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"} \ No newline at end of file diff --git a/openecomp-ui/resources/fonts/fontawesome-webfont.eot b/openecomp-ui/resources/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000..33b2bb8005 Binary files /dev/null and b/openecomp-ui/resources/fonts/fontawesome-webfont.eot differ diff --git a/openecomp-ui/resources/fonts/fontawesome-webfont.svg b/openecomp-ui/resources/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000000..1ee89d4368 --- /dev/null +++ b/openecomp-ui/resources/fonts/fontawesome-webfont.svg @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openecomp-ui/resources/fonts/fontawesome-webfont.ttf b/openecomp-ui/resources/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000..ed9372f8ea Binary files /dev/null and b/openecomp-ui/resources/fonts/fontawesome-webfont.ttf differ diff --git a/openecomp-ui/resources/fonts/fontawesome-webfont.woff b/openecomp-ui/resources/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000..8b280b98fa Binary files /dev/null and b/openecomp-ui/resources/fonts/fontawesome-webfont.woff differ diff --git a/openecomp-ui/resources/fonts/fontawesome-webfont.woff2 b/openecomp-ui/resources/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000000..3311d58514 Binary files /dev/null and b/openecomp-ui/resources/fonts/fontawesome-webfont.woff2 differ diff --git a/openecomp-ui/resources/fonts/omnes-att-bold-italic.otf b/openecomp-ui/resources/fonts/omnes-att-bold-italic.otf new file mode 100644 index 0000000000..77f0dbc15f Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-bold-italic.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-bold.otf b/openecomp-ui/resources/fonts/omnes-att-bold.otf new file mode 100644 index 0000000000..136afca84c Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-bold.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-italic.otf b/openecomp-ui/resources/fonts/omnes-att-italic.otf new file mode 100644 index 0000000000..5dc1da79d4 Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-italic.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-light-Italic.otf b/openecomp-ui/resources/fonts/omnes-att-light-Italic.otf new file mode 100644 index 0000000000..b13ae4fede Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-light-Italic.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-light.otf b/openecomp-ui/resources/fonts/omnes-att-light.otf new file mode 100644 index 0000000000..587d871e6f Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-light.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-medium-italic.otf b/openecomp-ui/resources/fonts/omnes-att-medium-italic.otf new file mode 100644 index 0000000000..f824bc23e7 Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-medium-italic.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-medium.otf b/openecomp-ui/resources/fonts/omnes-att-medium.otf new file mode 100644 index 0000000000..3085c1fa39 Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-medium.otf differ diff --git a/openecomp-ui/resources/fonts/omnes-att-regular.otf b/openecomp-ui/resources/fonts/omnes-att-regular.otf new file mode 100644 index 0000000000..a1a78eb7ca Binary files /dev/null and b/openecomp-ui/resources/fonts/omnes-att-regular.otf differ diff --git a/openecomp-ui/resources/images/ecomp/ASDC_Sprite.png b/openecomp-ui/resources/images/ecomp/ASDC_Sprite.png new file mode 100644 index 0000000000..db4440427c Binary files /dev/null and b/openecomp-ui/resources/images/ecomp/ASDC_Sprite.png differ diff --git a/openecomp-ui/resources/images/ecomp/sprite-services-icons.png b/openecomp-ui/resources/images/ecomp/sprite-services-icons.png new file mode 100644 index 0000000000..afb3cbc286 Binary files /dev/null and b/openecomp-ui/resources/images/ecomp/sprite-services-icons.png differ diff --git a/openecomp-ui/resources/images/icons/favicon.ico b/openecomp-ui/resources/images/icons/favicon.ico new file mode 100644 index 0000000000..c59a7aa0a3 Binary files /dev/null and b/openecomp-ui/resources/images/icons/favicon.ico differ diff --git a/openecomp-ui/resources/images/icons/favicon.png b/openecomp-ui/resources/images/icons/favicon.png new file mode 100644 index 0000000000..c59a7aa0a3 Binary files /dev/null and b/openecomp-ui/resources/images/icons/favicon.png differ diff --git a/openecomp-ui/resources/images/onboarding/vendor-license-model.svg b/openecomp-ui/resources/images/onboarding/vendor-license-model.svg new file mode 100644 index 0000000000..f23c30e05e --- /dev/null +++ b/openecomp-ui/resources/images/onboarding/vendor-license-model.svg @@ -0,0 +1 @@ +vendoe license model icon \ No newline at end of file diff --git a/openecomp-ui/resources/images/onboarding/vendor-software-product.svg b/openecomp-ui/resources/images/onboarding/vendor-software-product.svg new file mode 100644 index 0000000000..a547c4abdf --- /dev/null +++ b/openecomp-ui/resources/images/onboarding/vendor-software-product.svg @@ -0,0 +1 @@ +vendor software product \ No newline at end of file diff --git a/openecomp-ui/resources/images/plus-circle-icon.svg b/openecomp-ui/resources/images/plus-circle-icon.svg new file mode 100644 index 0000000000..352dcad5e8 --- /dev/null +++ b/openecomp-ui/resources/images/plus-circle-icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/openecomp-ui/resources/scss/_common.scss b/openecomp-ui/resources/scss/_common.scss new file mode 100644 index 0000000000..6ade0abfe5 --- /dev/null +++ b/openecomp-ui/resources/scss/_common.scss @@ -0,0 +1,6 @@ +@import "common/variables"; +@import "common/typography"; +@import "common/base"; +@import "common/layout"; +@import "common/utils"; + diff --git a/openecomp-ui/resources/scss/_components.scss b/openecomp-ui/resources/scss/_components.scss new file mode 100644 index 0000000000..884308885a --- /dev/null +++ b/openecomp-ui/resources/scss/_components.scss @@ -0,0 +1,73 @@ +@import "components/punchOut"; +@import "components/buttons"; +@import "components/forms"; +@import "components/validationForm"; +@import "components/slidePanel"; +@import "components/dualListBox"; +@import "components/listEditorView"; +@import "components/toggleInput"; +@import "components/notifications"; +@import "components/inputOptions"; +@import "components/progressBar"; +@import "components/versionController"; +@import "components/sequenceDiagram"; +@import "components/navigationSideBar"; +@import "components/loader"; +@import "components/dropzone"; +@import "components/submitErrorResponse"; +@import "components/expandableInput"; + +%noselect { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.clickable { + cursor: pointer; +} + +.no-padding { + padding: 0; +} + +.next-to-icon-label { + $margin: 10px; + margin-left: $margin; + &.right { + margin-left: 0; + margin-right: $margin; + } +} + +.search-wrapper { + display: flex; + .search-input-control { + flex: 1 1; + margin: 0; + .form-control { + font-style: italic; + } + } + .search-icon { + position: relative; + left: -20px; + align-self: center; + width: 0; + color: $dark-gray; + } + .filter-icon { + position: relative; + left: -20px; + align-self: center; + width: 0; + background-color: $white; + } +} + +.warning-icon { + margin-left: 50%; + color: $yellow; +} diff --git a/openecomp-ui/resources/scss/_modules.scss b/openecomp-ui/resources/scss/_modules.scss new file mode 100644 index 0000000000..fd35bf2e0b --- /dev/null +++ b/openecomp-ui/resources/scss/_modules.scss @@ -0,0 +1,22 @@ +@import "modules/licenseAgreement"; +@import "modules/featureGroup"; +@import "modules/entitlementPools"; +@import "modules/licenseKeyGroup"; +@import "modules/softwareProductLandingPage"; +@import "modules/softwareProductCreatePage"; +@import "modules/_softwareProductAttachmentPage"; +@import "modules/_softwareProductProcessesPage"; +@import "modules/_vspComponentQuestionnaire"; +@import "modules/_softwareProductNetworksPage"; +@import "modules/_softwareProductComponentNetwork"; +@import "modules/_softwareProductComponentGeneral"; +@import "modules/_softwareproductComponentLoadBalancing"; +@import "modules/vspComponentMonitoring"; +@import "modules/licenseModel"; +@import "modules/onboardingCatalog"; +@import "modules/workflows"; +@import "modules/uploadScreen"; + + + + diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_buttons.scss b/openecomp-ui/resources/scss/bootstrap-cust/_buttons.scss new file mode 100644 index 0000000000..bf58006d89 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_buttons.scss @@ -0,0 +1,13 @@ + +.btn { + height: 30px; + min-width: 95px; +} + +.btn-default .fa { + color: $brand-primary; +} + +.btn-info { + @include button-variant($text-black, $tlv-gray, $light-gray); +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_close.scss b/openecomp-ui/resources/scss/bootstrap-cust/_close.scss new file mode 100644 index 0000000000..38d7138d86 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_close.scss @@ -0,0 +1,7 @@ +// +// Close icons +// -------------------------------------------------- + +.close { + font-size: $heading-font-1; +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_dropdowns.scss b/openecomp-ui/resources/scss/bootstrap-cust/_dropdowns.scss new file mode 100644 index 0000000000..d6f895261c --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_dropdowns.scss @@ -0,0 +1,16 @@ + +.caret { + right: 8px; + top: 12px; + position: absolute; +} + +.dropdown-menu { + padding: 0; + li a { + height: 30px; + } + .divider { + margin: 0; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_forms.scss b/openecomp-ui/resources/scss/bootstrap-cust/_forms.scss new file mode 100644 index 0000000000..545b23ee7f --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_forms.scss @@ -0,0 +1,110 @@ +.form-group { + .control-label { + @extend .body-2-medium; + } + &.required { + label:before { + content: "*"; + color: $red; + margin: 0 4px 0 0; + } + } +} +.form-control { + @extend .body-1; + border-radius: 2px; + height: 30px; + @include box-shadow(none); + &:focus { + @include box-shadow(none); + } + &:hover { + border-color: $gray; + } +} + +label { + @extend .body-3; + margin-bottom: 8px; +} + +select.form-control { + display: block; + width: 215px; +} + +select[multiple] { + background: none; +} + +input[type="radio"], input[type="checkbox"] { + margin: 0; + + &:before { + content: ""; + display: inline-block; + width: 11px; + height: 11px; + margin-right: 10px; + position: absolute; + background-color: $white; + border: 1px solid $blue; + box-sizing: content-box; + } +} + +.radio label { + font-weight: normal; + display: inline-block; + cursor: pointer; + margin-right: $body-font-1; + font-size: $body-font-1; +} + +.checkbox label { + font-weight: normal; + display: inline-block; + cursor: pointer; + margin-right: $body-font-1; + font-size: $body-font-1; +} + +.radio input:before { + border-radius: 8px; +} + +.checkbox input:before { + border-radius: 2px; +} + +input[type=radio]:checked:before { + content: "\2022"; + color: $blue; + font-size: 30px; + text-align: center; + line-height: 11px; + font-family: $radio-font-family; +} + +input[type=checkbox]:checked:before { + font-family: $icon-font-family; + content: "\f00c"; + font-size: $icon-font-size; + color: $blue; + text-align: center; +} + +.radio, +.checkbox { + margin-top: 0px; + margin-bottom: 0px; +} + +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + top: 2px; +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_list-group.scss b/openecomp-ui/resources/scss/bootstrap-cust/_list-group.scss new file mode 100644 index 0000000000..b94f91bac9 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_list-group.scss @@ -0,0 +1,5 @@ + +.list-group-item { + border: none; + border-top: 3px solid $list-group-border; +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_mixins.scss b/openecomp-ui/resources/scss/bootstrap-cust/_mixins.scss new file mode 100644 index 0000000000..774e662014 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_mixins.scss @@ -0,0 +1,3 @@ + + +@import "mixins/buttons"; \ No newline at end of file diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss b/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss new file mode 100644 index 0000000000..6bc6e46b2f --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_modals.scss @@ -0,0 +1,18 @@ +.modal-content { + .modal-header { + border-top: 3px solid $blue; + .modal-title { + @extend .heading-2; + } + } + + .modal-body{ + padding: 15px; + } + + .modal-footer { + padding: 15px; + border-top: 0; + background-color: $tlv-gray; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_navbar.scss b/openecomp-ui/resources/scss/bootstrap-cust/_navbar.scss new file mode 100644 index 0000000000..acf2024b56 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_navbar.scss @@ -0,0 +1,3 @@ +.navbar { + border: none; +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_navs.scss b/openecomp-ui/resources/scss/bootstrap-cust/_navs.scss new file mode 100644 index 0000000000..7b9cff963d --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_navs.scss @@ -0,0 +1,30 @@ +.nav { + > li { + > a { + padding: 10px 10px 3px; + } + } +} + +.nav-tabs { + @include box-shadow(0px 2px 1px -1px $gray); + padding: 0 28px; + + > li { + @extend .body-1; + + > a { + color: $dark-gray; + text-transform: uppercase; + } + + &.active > a { + &, + &:hover, + &:focus { + @extend .body-1-medium; + border-bottom: 3px solid $blue; + } + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_panels.scss b/openecomp-ui/resources/scss/bootstrap-cust/_panels.scss new file mode 100644 index 0000000000..9ee5622292 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_panels.scss @@ -0,0 +1,3 @@ +.panel { + border: none; +} \ No newline at end of file diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_tables.scss b/openecomp-ui/resources/scss/bootstrap-cust/_tables.scss new file mode 100644 index 0000000000..6902914f35 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_tables.scss @@ -0,0 +1,42 @@ +.table { + // Cells + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + border-top: none; + } + } + } + // Bottom align for column headings + > thead > tr > th { + border-bottom: none; + } + // Account for multiple tbody instances + > tbody + tbody { + border-top: none; + } + + +} + +.table-striped { + > tbody > tr:nth-of-type( odd ) { + background-color: $background-gray; + } +} + +.table-striped { + > tbody > tr:nth-of-type( even ) { + background-color: $gray; + } +} + +.table-hover { + > tbody > tr:hover { + background-color: $tlv-hover; + cursor: pointer; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap-cust/_variables.scss b/openecomp-ui/resources/scss/bootstrap-cust/_variables.scss new file mode 100644 index 0000000000..59f0e3db7c --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap-cust/_variables.scss @@ -0,0 +1,127 @@ + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +$gray-darker: $dark-gray; +$gray-dark: $dark-gray; +$gray: $gray; +$gray-light: $light-gray; +$gray-lighter: $light-gray; + +$brand-primary: $blue; +$brand-success: $green; +$brand-info: $light-gray; +$brand-warning: $yellow; +$brand-danger: $red; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +$body-bg: $white; +$text-color: $text-black; +$link-color: $link-blue; +$link-hover-color: $blue; + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. +$font-family-sans-serif: omnes-regular, "Omnes-Regular", "Helvetica Neue", Helvetica, Arial, sans-serif; +//$font-family-base: $font-family-sans-serif !default; +$font-size-base: $body-font-2; +$font-size-large: $body-font-1; +$font-size-small: $body-font-3; +$font-size-h1: $heading-font-1; +$font-size-h2: $heading-font-2; +$font-size-h3: $heading-font-4; +$font-size-h4: $heading-font-5; + + + +//== Components +// +//## Define common padding and border radius sizes and more. +$border-radius-base: 0; +$border-radius-large: 0; +$border-radius-small: 0; + + +//== Buttons +// +//## For each of Bootstrap's buttons, define text, background and border color. + +// $btn-font-weight: normal !default; +$btn-default-color: $text-color; +$btn-default-bg: $white; +$btn-default-border: $light-gray; + +$btn-success-color: $white; + +// Allows for customizing button radius independently from global border radius +$btn-border-radius-base: 2px; +$btn-border-radius-large: 2px; +$btn-border-radius-small: 2px; + +//== Dropdowns +// +$dropdown-bg: $white; +$dropdown-border: $link-blue; +$dropdown-link-color: $text-black; +$dropdown-divider-bg: $gray; +//** Hover color for dropdown links. +$dropdown-link-hover-color: $black; +//** Hover background for dropdown links. +$dropdown-link-hover-bg: $tlv-hover; + +//** Active dropdown menu item text color. +$dropdown-link-active-color: $black; +//** Active dropdown menu item background color. +$dropdown-link-active-bg: $tlv-hover; + +//== Forms +// +//## +$form-group-margin-bottom: 24px; + + +$input-bg: $white; +$input-bg-disabled: $tlv-light-gray; +$input-color: $dark-gray; +$input-border: $light-gray; +$input-border-focus: $dark-blue; + +//== Modals +// +//## +$modal-content-bg: $white; +$modal-inner-padding: 0 15px; +$modal-title-padding: 30px 25px 10px 25px; + + +//== Close +// +//## +$close-color: $tlv-light-gray; + +//== Navs +// +//## +//=== Shared nav styles +$nav-link-hover-bg: transparent; + +$navbar-inverse-bg: $gray; + +//== Tabs +$nav-tabs-border-color: transparent; +$nav-tabs-link-hover-border-color: transparent; +$nav-tabs-active-link-hover-bg: transparent; +$nav-tabs-active-link-hover-color: $text-black; +$nav-tabs-active-link-hover-border-color: transparent; + + +//== Popovers +// +//## +$popover-bg: $background-gray; diff --git a/openecomp-ui/resources/scss/bootstrap.scss b/openecomp-ui/resources/scss/bootstrap.scss new file mode 100644 index 0000000000..a8c470216d --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap.scss @@ -0,0 +1,61 @@ +// DOX CORE +@import "common/variables"; +@import "common/typography"; + +// Core variables and mixins +@import "bootstrap-cust/variables"; +@import "bootstrap/variables"; +@import "bootstrap/mixins"; +// Reset and dependencies +@import "bootstrap/normalize"; +//@import "bootstrap/print"; +//@import "bootstrap/glyphicons"; + +// Core CSS +@import "bootstrap/scaffolding"; +@import "bootstrap/type"; +@import "bootstrap/code"; +@import "bootstrap/grid"; +@import "bootstrap/tables"; +@import "bootstrap-cust/tables"; +@import "bootstrap/forms"; +@import "bootstrap-cust/forms"; +@import "bootstrap/buttons"; +@import "bootstrap-cust/buttons"; +// Components +@import "bootstrap/component-animations"; +@import "bootstrap/dropdowns"; +@import "bootstrap-cust/dropdowns"; +@import "bootstrap/button-groups"; +@import "bootstrap/input-groups"; +@import "bootstrap/navs"; +@import "bootstrap-cust/navs"; +@import "bootstrap/navbar"; +@import "bootstrap-cust/navbar"; +@import "bootstrap/breadcrumbs"; +@import "bootstrap/pagination"; +@import "bootstrap/pager"; +@import "bootstrap/labels"; +@import "bootstrap/badges"; +@import "bootstrap/jumbotron"; +// @import "bootstrap/thumbnails"; +@import "bootstrap/alerts"; +@import "bootstrap/progress-bars"; +@import "bootstrap/media"; +@import "bootstrap/list-group"; +@import "bootstrap-cust/list-group"; +@import "bootstrap/panels"; +@import "bootstrap-cust/panels"; +// @import "bootstrap/responsive-embed"; +//@import "bootstrap/wells"; +@import "bootstrap/close"; +@import "bootstrap-cust/close"; +// Components w/ JavaScript +@import "bootstrap/modals"; +@import "bootstrap-cust/modals"; +@import "bootstrap/tooltip"; +@import "bootstrap/popovers"; +// @import "bootstrap/carousel"; +// Utility classes +@import "bootstrap/utilities"; +@import "bootstrap/responsive-utilities"; diff --git a/openecomp-ui/resources/scss/bootstrap/_alerts.scss b/openecomp-ui/resources/scss/bootstrap/_alerts.scss new file mode 100644 index 0000000000..206ec3226a --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_alerts.scss @@ -0,0 +1,72 @@ +// +// Alerts +// -------------------------------------------------- + +// Base styles +// ------------------------- + +.alert { + padding: $alert-padding; + margin-bottom: $line-height-computed; + border: 1px solid transparent; + border-radius: $alert-border-radius; + + // Headings for larger alerts + h4 { + margin-top: 0; + // Specified for the h4 to prevent conflicts of changing $headings-color + color: inherit; + } + + // Provide class for links that match alerts + .alert-link { + font-weight: $alert-link-font-weight; + } + + // Improve alignment and spacing of inner content + > p, + > ul { + margin-bottom: 0; + } + + > p + p { + margin-top: 5px; + } +} + +// Dismissible alerts +// +// Expand the right padding and account for the close button's positioning. + +.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. +.alert-dismissible { + padding-right: ($alert-padding + 20); + + // Adjust close link position + .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; + } +} + +// Alternate styles +// +// Generate contextual modifier classes for colorizing the alert. + +.alert-success { + @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); +} + +.alert-info { + @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); +} + +.alert-warning { + @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text); +} + +.alert-danger { + @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_badges.scss b/openecomp-ui/resources/scss/bootstrap/_badges.scss new file mode 100644 index 0000000000..ad595a9557 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_badges.scss @@ -0,0 +1,67 @@ +// +// Badges +// -------------------------------------------------- + +// Base class +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: $font-size-small; + font-weight: $badge-font-weight; + color: $badge-color; + line-height: $badge-line-height; + vertical-align: middle; + white-space: nowrap; + text-align: center; + background-color: $badge-bg; + border-radius: $badge-border-radius; + + // Empty badges collapse automatically (not available in IE8) + &:empty { + display: none; + } + + // Quick fix for badges in buttons + .btn & { + position: relative; + top: -1px; + } + + .btn-xs &, + .btn-group-xs > .btn & { + top: 0; + padding: 1px 5px; + } + + // [converter] extracted a& to a.badge + + // Account for badges in navs + .list-group-item.active > &, + .nav-pills > .active > a > & { + color: $badge-active-color; + background-color: $badge-active-bg; + } + + .list-group-item > & { + float: right; + } + + .list-group-item > & + & { + margin-right: 5px; + } + + .nav-pills > li > a > & { + margin-left: 3px; + } +} + +// Hover state, but only for links +a.badge { + &:hover, + &:focus { + color: $badge-link-hover-color; + text-decoration: none; + cursor: pointer; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_breadcrumbs.scss b/openecomp-ui/resources/scss/bootstrap/_breadcrumbs.scss new file mode 100644 index 0000000000..2041f0dc84 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_breadcrumbs.scss @@ -0,0 +1,25 @@ +// +// Breadcrumbs +// -------------------------------------------------- + +.breadcrumb { + padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal; + margin-bottom: $line-height-computed; + list-style: none; + background-color: $breadcrumb-bg; + border-radius: $border-radius-base; + + > li { + display: inline-block; + + + li:before { + content: "#{$breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space + padding: 0 5px; + color: $breadcrumb-color; + } + } + + > .active { + color: $breadcrumb-active-color; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_button-groups.scss b/openecomp-ui/resources/scss/bootstrap/_button-groups.scss new file mode 100644 index 0000000000..dc2906e44f --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_button-groups.scss @@ -0,0 +1,256 @@ +// +// Button groups +// -------------------------------------------------- + +// Make the div behave like a button +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; // match .btn alignment given font-size hack above + > .btn { + position: relative; + float: left; + // Bring the "active" button to the front + &:hover, + &:focus, + &:active, + &.active { + z-index: 2; + } + } +} + +// Prevent double borders when buttons are next to each other +.btn-group { + .btn + .btn, + .btn + .btn-group, + .btn-group + .btn, + .btn-group + .btn-group { + margin-left: -1px; + } +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + margin-left: -5px; // Offset the first child's margin + @include clearfix; + + .btn, + .btn-group, + .input-group { + float: left; + } + > .btn, + > .btn-group, + > .input-group { + margin-left: 5px; + } +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match +.btn-group > .btn:first-child { + margin-left: 0; + &:not(:last-child):not(.dropdown-toggle) { + @include border-right-radius(0); + } +} + +// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + @include border-left-radius(0); +} + +// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) +.btn-group > .btn-group { + float: left; +} + +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + @include border-right-radius(0); + } +} + +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + @include border-left-radius(0); +} + +// On active and open, don't show outline +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +// Sizing +// +// Remix the default button sizing classes into new ones for easier manipulation. + +.btn-group-xs > .btn { + @extend .btn-xs; +} + +.btn-group-sm > .btn { + @extend .btn-sm; +} + +.btn-group-lg > .btn { + @extend .btn-lg; +} + +// Split button dropdowns +// ---------------------- + +// Give the line between buttons some depth +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} + +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} + +// The clickable button for toggling the menu +// Remove the gradient and set the same inset shadow as the :active state +.btn-group.open .dropdown-toggle { + @include box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125)); + + // Show no shadow for `.btn-link` since it has no other button styles. + &.btn-link { + @include box-shadow(none); + } +} + +// Reposition the caret +.btn .caret { + margin-left: 0; +} + +// Carets in other button sizes +.btn-lg .caret { + border-width: $caret-width-large $caret-width-large 0; + border-bottom-width: 0; +} + +// Upside down carets for .dropup +.dropup .btn-lg .caret { + border-width: 0 $caret-width-large $caret-width-large; +} + +// Vertical button groups +// ---------------------- + +.btn-group-vertical { + > .btn, + > .btn-group, + > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; + } + + // Clear floats so dropdown menus can be properly placed + > .btn-group { + @include clearfix; + > .btn { + float: none; + } + } + + > .btn + .btn, + > .btn + .btn-group, + > .btn-group + .btn, + > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; + } +} + +.btn-group-vertical > .btn { + &:not(:first-child):not(:last-child) { + border-radius: 0; + } + &:first-child:not(:last-child) { + border-top-right-radius: $btn-border-radius-base; + @include border-bottom-radius(0); + } + &:last-child:not(:first-child) { + border-bottom-left-radius: $btn-border-radius-base; + @include border-top-radius(0); + } +} + +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group-vertical > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + @include border-bottom-radius(0); + } +} + +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + @include border-top-radius(0); +} + +// Justified button groups +// ---------------------- + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; + > .btn, + > .btn-group { + float: none; + display: table-cell; + width: 1%; + } + > .btn-group .btn { + width: 100%; + } + + > .btn-group .dropdown-menu { + left: auto; + } +} + +// Checkbox and radio options +// +// In order to support the browser's form validation feedback, powered by the +// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use +// `display: none;` or `visibility: hidden;` as that also hides the popover. +// Simply visually hiding the inputs via `opacity` would leave them clickable in +// certain cases which is prevented by using `clip` and `pointer-events`. +// This way, we ensure a DOM element is visible to position the popover from. +// +// See https://github.com/twbs/bootstrap/pull/12794 and +// https://github.com/twbs/bootstrap/pull/14559 for more information. + +[data-toggle="buttons"] { + > .btn, + > .btn-group > .btn { + input[type="radio"], + input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_buttons.scss b/openecomp-ui/resources/scss/bootstrap/_buttons.scss new file mode 100644 index 0000000000..a9c3f340d7 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_buttons.scss @@ -0,0 +1,170 @@ +// +// Buttons +// -------------------------------------------------- + +// Base styles +// -------------------------------------------------- + +.btn { + display: inline-block; + margin-bottom: 0; // For input.btn + font-weight: $btn-font-weight; + text-align: center; + vertical-align: middle; + touch-action: manipulation; + cursor: pointer; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + white-space: nowrap; + @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); + @include user-select(none); + + &, + &:active, + &.active { + &:focus, + &.focus { + @include tab-focus; + } + } + + &:hover, + &:focus, + &.focus { + color: $btn-default-color; + text-decoration: none; + } + + &:active, + &.active { + outline: 0; + background-image: none; + @include box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125)); + } + + &.disabled, + &[disabled], + fieldset[disabled] & { + cursor: $cursor-disabled; + @include opacity(.65); + @include box-shadow(none); + } + + // [converter] extracted a& to a.btn +} + +a.btn { + &.disabled, + fieldset[disabled] & { + pointer-events: none; // Future-proof disabling of clicks on `` elements + } +} + +// Alternate buttons +// -------------------------------------------------- + +.btn-default { + @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); +} + +.btn-primary { + @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); +} + +// Success appears as green +.btn-success { + @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border); +} + +// Info appears as blue-green +.btn-info { + @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border); +} + +// Warning appears as orange +.btn-warning { + @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border); +} + +// Danger and error appear as red +.btn-danger { + @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border); +} + +// Link buttons +// ------------------------- + +// Make a button look and behave like a link +.btn-link { + color: $link-color; + font-weight: normal; + border-radius: 0; + + &, + &:active, + &.active, + &[disabled], + fieldset[disabled] & { + background-color: transparent; + @include box-shadow(none); + } + &, + &:hover, + &:focus, + &:active { + border-color: transparent; + } + &:hover, + &:focus { + color: $link-hover-color; + text-decoration: $link-hover-decoration; + background-color: transparent; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: $btn-link-disabled-color; + text-decoration: none; + } + } +} + +// Button Sizes +// -------------------------------------------------- + +.btn-lg { + // line-height: ensure even-numbered height of button next to large input + @include button-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $btn-border-radius-large); +} + +.btn-sm { + // line-height: ensure proper height of button next to small input + @include button-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $btn-border-radius-small); +} + +.btn-xs { + @include button-size($padding-xs-vertical, $padding-xs-horizontal, $font-size-small, $line-height-small, $btn-border-radius-small); +} + +// Block button +// -------------------------------------------------- + +.btn-block { + display: block; + width: 100%; +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_carousel.scss b/openecomp-ui/resources/scss/bootstrap/_carousel.scss new file mode 100644 index 0000000000..5542ceb905 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_carousel.scss @@ -0,0 +1,266 @@ +// +// Carousel +// -------------------------------------------------- + +// Wrapper for the slide container and indicators +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; + + > .item { + display: none; + position: relative; + @include transition(.6s ease-in-out left); + + // Account for jankitude on images + > img, + > a > img { + @include img-responsive; + line-height: 1; + } + + // WebKit CSS3 transforms for supported devices + @media all and (transform-3d), (-webkit-transform-3d) { + @include transition-transform(0.6s ease-in-out); + @include backface-visibility(hidden); + @include perspective(1000px); + + &.next, + &.active.right { + @include translate3d(100%, 0, 0); + left: 0; + } + &.prev, + &.active.left { + @include translate3d(-100%, 0, 0); + left: 0; + } + &.next.left, + &.prev.right, + &.active { + @include translate3d(0, 0, 0); + left: 0; + } + } + } + + > .active, + > .next, + > .prev { + display: block; + } + + > .active { + left: 0; + } + + > .next, + > .prev { + position: absolute; + top: 0; + width: 100%; + } + + > .next { + left: 100%; + } + > .prev { + left: -100%; + } + > .next.left, + > .prev.right { + left: 0; + } + + > .active.left { + left: -100%; + } + > .active.right { + left: 100%; + } + +} + +// Left/right controls for nav +// --------------------------- + +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: $carousel-control-width; + @include opacity($carousel-control-opacity); + font-size: $carousel-control-font-size; + color: $carousel-control-color; + text-align: center; + text-shadow: $carousel-text-shadow; + // We can't have this transition here because WebKit cancels the carousel + // animation if you trip this while in the middle of another animation. + + // Set gradients for backgrounds + &.left { + @include gradient-horizontal($start-color: rgba(0, 0, 0, .5), $end-color: rgba(0, 0, 0, .0001)); + } + &.right { + left: auto; + right: 0; + @include gradient-horizontal($start-color: rgba(0, 0, 0, .0001), $end-color: rgba(0, 0, 0, .5)); + } + + // Hover/focus state + &:hover, + &:focus { + outline: 0; + color: $carousel-control-color; + text-decoration: none; + @include opacity(.9); + } + + // Toggles + .icon-prev, + .icon-next, + .glyphicon-chevron-left, + .glyphicon-chevron-right { + position: absolute; + top: 50%; + margin-top: -10px; + z-index: 5; + display: inline-block; + } + .icon-prev, + .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; + } + .icon-next, + .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; + } + .icon-prev, + .icon-next { + width: 20px; + height: 20px; + line-height: 1; + font-family: serif; + } + + .icon-prev { + &:before { + content: '\2039'; // SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) + } + } + .icon-next { + &:before { + content: '\203a'; // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) + } + } +} + +// Optional indicator pips +// +// Add an unordered list with the following class and add a list item for each +// slide your carousel holds. + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; + + li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid $carousel-indicator-border-color; + border-radius: 10px; + cursor: pointer; + + // IE8-9 hack for event handling + // + // Internet Explorer 8-9 does not support clicks on elements without a set + // `background-color`. We cannot use `filter` since that's not viewed as a + // background color by the browser. Thus, a hack is needed. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer + // + // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we + // set alpha transparency for the best results possible. + background-color: #000 \9; // IE8 + background-color: rgba(0, 0, 0, 0); // IE9 + } + .active { + margin: 0; + width: 12px; + height: 12px; + background-color: $carousel-indicator-active-bg; + } +} + +// Optional captions +// ----------------------------- +// Hidden by default for smaller viewports +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: $carousel-caption-color; + text-align: center; + text-shadow: $carousel-text-shadow; + & .btn { + text-shadow: none; // No shadow for button elements in carousel-caption + } +} + +// Scale up controls for tablets and up +@media screen and (min-width: $screen-sm-min) { + + // Scale up the controls a smidge + .carousel-control { + .glyphicon-chevron-left, + .glyphicon-chevron-right, + .icon-prev, + .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .glyphicon-chevron-left, + .icon-prev { + margin-left: -15px; + } + .glyphicon-chevron-right, + .icon-next { + margin-right: -15px; + } + } + + // Show and left align the captions + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + + // Move up the indicators + .carousel-indicators { + bottom: 20px; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_close.scss b/openecomp-ui/resources/scss/bootstrap/_close.scss new file mode 100644 index 0000000000..d3ea6ab0ba --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_close.scss @@ -0,0 +1,35 @@ +// +// Close icons +// -------------------------------------------------- + +.close { + float: right; + font-size: ($font-size-base * 1.5); + font-weight: $close-font-weight; + line-height: 1; + color: $close-color; + text-shadow: $close-text-shadow; + @include opacity(.2); + + &:hover, + &:focus { + color: $close-color; + text-decoration: none; + cursor: pointer; + @include opacity(.5); + } + + // [converter] extracted button& to button.close +} + +// Additional properties for button version +// iOS requires the button element instead of an anchor tag. +// If you want the anchor version, it requires `href="#"`. +// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_code.scss b/openecomp-ui/resources/scss/bootstrap/_code.scss new file mode 100644 index 0000000000..ab74c19e74 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_code.scss @@ -0,0 +1,68 @@ +// +// Code (inline and block) +// -------------------------------------------------- + +// Inline and block code styles +code, +kbd, +pre, +samp { + font-family: $font-family-monospace; +} + +// Inline code +code { + padding: 2px 4px; + font-size: 90%; + color: $code-color; + background-color: $code-bg; + border-radius: $border-radius-base; +} + +// User input typically entered via keyboard +kbd { + padding: 2px 4px; + font-size: 90%; + color: $kbd-color; + background-color: $kbd-bg; + border-radius: $border-radius-small; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + + kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + box-shadow: none; + } +} + +// Blocks of code +pre { + display: block; + padding: (($line-height-computed - 1) / 2); + margin: 0 0 ($line-height-computed / 2); + font-size: ($font-size-base - 1); // 14px to 13px + line-height: $line-height-base; + word-break: break-all; + word-wrap: break-word; + color: $pre-color; + background-color: $pre-bg; + border: 1px solid $pre-border-color; + border-radius: $border-radius-base; + + // Account for some code outputs that place code tags in pre tags + code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; + } +} + +// Enable scrollable blocks of code +.pre-scrollable { + max-height: $pre-scrollable-max-height; + overflow-y: scroll; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_component-animations.scss b/openecomp-ui/resources/scss/bootstrap/_component-animations.scss new file mode 100644 index 0000000000..58befaa2ef --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_component-animations.scss @@ -0,0 +1,43 @@ +// +// Component animations +// -------------------------------------------------- + +// Heads up! +// +// We don't use the `.opacity()` mixin here since it causes a bug with text +// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. + +.fade { + opacity: 0; + @include transition(opacity .15s linear); + &.in { + opacity: 1; + } +} + +.collapse { + display: none; + + &.in { + display: block; + } + // [converter] extracted tr&.in to tr.collapse.in + // [converter] extracted tbody&.in to tbody.collapse.in +} + +tr.collapse.in { + display: table-row; +} + +tbody.collapse.in { + display: table-row-group; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + @include transition-property(height, visibility); + @include transition-duration(.35s); + @include transition-timing-function(ease); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_dropdowns.scss b/openecomp-ui/resources/scss/bootstrap/_dropdowns.scss new file mode 100644 index 0000000000..6fcfb9f48e --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_dropdowns.scss @@ -0,0 +1,217 @@ +// +// Dropdown menus +// -------------------------------------------------- + +// Dropdown arrow/caret +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: $caret-width-base dashed; + border-top: $caret-width-base solid \9; // IE8 + border-right: $caret-width-base solid transparent; + border-left: $caret-width-base solid transparent; +} + +// The dropdown wrapper (div) +.dropup, +.dropdown { + position: relative; +} + +// Prevent the focus on the dropdown toggle when closing dropdowns +.dropdown-toggle:focus { + outline: 0; +} + +// The dropdown menu (ul) +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: $zindex-dropdown; + display: none; // none by default, but block on "open" of the menu + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; // override default ul + list-style: none; + font-size: $font-size-base; + text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) + background-color: $dropdown-bg; + border: 1px solid $dropdown-fallback-border; // IE8 fallback + border: 1px solid $dropdown-border; + border-radius: $border-radius-base; + @include box-shadow(0 6px 12px rgba(0, 0, 0, .175)); + background-clip: padding-box; + + // Aligns the dropdown menu to right + // + // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` + &.pull-right { + right: 0; + left: auto; + } + + // Dividers (basically an hr) within the dropdown + .divider { + @include nav-divider($dropdown-divider-bg); + } + + // Links within the dropdown menu + > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: $line-height-base; + color: $dropdown-link-color; + white-space: nowrap; // prevent links from randomly breaking onto new lines + } +} + +// Hover/Focus state +.dropdown-menu > li > a { + &:hover, + &:focus { + text-decoration: none; + color: $dropdown-link-hover-color; + background-color: $dropdown-link-hover-bg; + } +} + +// Active state +.dropdown-menu > .active > a { + &, + &:hover, + &:focus { + color: $dropdown-link-active-color; + text-decoration: none; + outline: 0; + background-color: $dropdown-link-active-bg; + } +} + +// Disabled state +// +// Gray out text and ensure the hover/focus state remains gray + +.dropdown-menu > .disabled > a { + &, + &:hover, + &:focus { + color: $dropdown-link-disabled-color; + } + + // Nuke hover/focus effects + &:hover, + &:focus { + text-decoration: none; + background-color: transparent; + background-image: none; // Remove CSS gradient + @include reset-filter; + cursor: $cursor-disabled; + } +} + +// Open state for the dropdown +.open { + // Show the menu + > .dropdown-menu { + display: block; + } + + // Remove the outline when :focus is triggered + > a { + outline: 0; + } +} + +// Menu positioning +// +// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown +// menu with the parent. +.dropdown-menu-right { + left: auto; // Reset the default from `.dropdown-menu` + right: 0; +} + +// With v3, we enabled auto-flipping if you have a dropdown within a right +// aligned nav component. To enable the undoing of that, we provide an override +// to restore the default dropdown menu alignment. +// +// This is only for left-aligning a dropdown menu within a `.navbar-right` or +// `.pull-right` nav component. +.dropdown-menu-left { + left: 0; + right: auto; +} + +// Dropdown section headers +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: $font-size-small; + line-height: $line-height-base; + color: $dropdown-header-color; + white-space: nowrap; // as with > li > a +} + +// Backdrop to catch body clicks on mobile, etc. +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: ($zindex-dropdown - 10); +} + +// Right aligned dropdowns +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// +// Just add .dropup after the standard .dropdown class and you're set, bro. +// TODO: abstract this so that the navbar fixed styles are not placed here? + +.dropup, +.navbar-fixed-bottom .dropdown { + // Reverse the caret + .caret { + border-top: 0; + border-bottom: $caret-width-base dashed; + border-bottom: $caret-width-base solid \9; // IE8 + content: ""; + } + // Different positioning for bottom up menu + .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; + } +} + +// Component alignment +// +// Reiterate per navbar.less and the modified component alignment there. + +@media (min-width: $grid-float-breakpoint) { + .navbar-right { + .dropdown-menu { + right: 0; + left: auto; + } + // Necessary for overrides of the default right aligned menu. + // Will remove come v4 in all likelihood. + .dropdown-menu-left { + left: 0; + right: auto; + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_forms.scss b/openecomp-ui/resources/scss/bootstrap/_forms.scss new file mode 100644 index 0000000000..042d4ec9bd --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_forms.scss @@ -0,0 +1,607 @@ +// +// Forms +// -------------------------------------------------- + +// Normalize non-controls +// +// Restyle and baseline non-control form elements. + +fieldset { + padding: 0; + margin: 0; + border: 0; + // Chrome and Firefox set a `min-width: min-content;` on fieldsets, + // so we reset that to ensure it behaves more like a standard block element. + // See https://github.com/twbs/bootstrap/issues/12359. + min-width: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: $line-height-computed; + font-size: ($font-size-base * 1.5); + line-height: inherit; + color: $legend-color; + border: 0; + border-bottom: 1px solid $legend-border-color; +} + +label { + display: inline-block; + max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) + margin-bottom: 5px; + font-weight: bold; +} + +// Normalize form controls +// +// While most of our form styles require extra classes, some basic normalization +// is required to ensure optimum display with or without those classes to better +// address browser inconsistencies. + +// Override content-box in Normalize (* isn't specific enough) +input[type="search"] { + @include box-sizing(border-box); +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; // IE8-9 + line-height: normal; +} + +input[type="file"] { + display: block; +} + +// Make range inputs behave like textual form controls +input[type="range"] { + display: block; + width: 100%; +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for file, radio, and checkbox +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + @include tab-focus; +} + +// Adjust output element +output { + display: block; + padding-top: ($padding-base-vertical + 1); + font-size: $font-size-base; + line-height: $line-height-base; + color: $input-color; +} + +// Common form controls +// +// Shared size and type resets for form controls. Apply `.form-control` to any +// of the following form controls: +// +// select +// textarea +// input[type="text"] +// input[type="password"] +// input[type="datetime"] +// input[type="datetime-local"] +// input[type="date"] +// input[type="month"] +// input[type="time"] +// input[type="week"] +// input[type="number"] +// input[type="email"] +// input[type="url"] +// input[type="search"] +// input[type="tel"] +// input[type="color"] + +.form-control { + display: block; + width: 100%; + height: $input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + padding: $padding-base-vertical $padding-base-horizontal; + font-size: $font-size-base; + line-height: $line-height-base; + color: $input-color; + background-color: $input-bg; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid $input-border; + border-radius: $input-border-radius; // Note: This has no effect on s in CSS. + @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075)); + @include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); + + // Customize the `:focus` state to imitate native WebKit styles. + @include form-control-focus; + + // Placeholder + @include placeholder; + + // Disabled and read-only inputs + // + // HTML5 says that controls under a fieldset > legend:first-child won't be + // disabled if the fieldset is disabled. Due to implementation difficulty, we + // don't honor that edge case; we style them as disabled anyway. + &[disabled], + &[readonly], + fieldset[disabled] & { + background-color: $input-bg-disabled; + opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655 + } + + &[disabled], + fieldset[disabled] & { + cursor: $cursor-disabled; + } + + // [converter] extracted textarea& to textarea.form-control +} + +// Reset height for `textarea`s +textarea.form-control { + height: auto; +} + +// Search inputs in iOS +// +// This overrides the extra rounded corners on search inputs in iOS so that our +// `.form-control` class can properly style them. Note that this cannot simply +// be added to `.form-control` as it's not specific enough. For details, see +// https://github.com/twbs/bootstrap/issues/11586. + +input[type="search"] { + -webkit-appearance: none; +} + +// Special styles for iOS temporal inputs +// +// In Mobile Safari, setting `display: block` on temporal inputs causes the +// text within the input to become vertically misaligned. As a workaround, we +// set a pixel line-height that matches the given height of the input, but only +// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 +// +// Note that as of 8.3, iOS doesn't support `datetime` or `week`. + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + &.form-control { + line-height: $input-height-base; + } + + &.input-sm, + .input-group-sm & { + line-height: $input-height-small; + } + + &.input-lg, + .input-group-lg & { + line-height: $input-height-large; + } + } +} + +// Form groups +// +// Designed to help with the organization and spacing of vertical forms. For +// horizontal forms, use the predefined grid classes. + +.form-group { + margin-bottom: $form-group-margin-bottom; +} + +// Checkboxes and radios +// +// Indent the labels to position radios/checkboxes as hanging controls. + +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; + + label { + min-height: $line-height-computed; // Ensure the input doesn't jump when there is no text + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; + } +} + +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9; +} + +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing +} + +// Radios and checkboxes on same line +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} + +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; // space out consecutive inline controls +} + +// Apply same disabled cursor tweak as for inputs +// Some special care is needed because Star + +@at-root { + // Import the fonts + @font-face { + font-family: 'Glyphicons Halflings'; + src: url(if($bootstrap-sass-asset-helper, twbs-font-path('#{$icon-font-path}#{$icon-font-name}.eot'), '#{$icon-font-path}#{$icon-font-name}.eot')); + src: url(if($bootstrap-sass-asset-helper, twbs-font-path('#{$icon-font-path}#{$icon-font-name}.eot?#iefix'), '#{$icon-font-path}#{$icon-font-name}.eot?#iefix')) format('embedded-opentype'), + url(if($bootstrap-sass-asset-helper, twbs-font-path('#{$icon-font-path}#{$icon-font-name}.woff2'), '#{$icon-font-path}#{$icon-font-name}.woff2')) format('woff2'), + url(if($bootstrap-sass-asset-helper, twbs-font-path('#{$icon-font-path}#{$icon-font-name}.woff'), '#{$icon-font-path}#{$icon-font-name}.woff')) format('woff'), + url(if($bootstrap-sass-asset-helper, twbs-font-path('#{$icon-font-path}#{$icon-font-name}.ttf'), '#{$icon-font-path}#{$icon-font-name}.ttf')) format('truetype'), + url(if($bootstrap-sass-asset-helper, twbs-font-path('#{$icon-font-path}#{$icon-font-name}.svg##{$icon-font-svg-id}'), '#{$icon-font-path}#{$icon-font-name}.svg##{$icon-font-svg-id}')) format('svg'); + } +} + +// Catchall baseclass +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Individual icons +.glyphicon-asterisk { + &:before { + content: "\2a"; + } +} + +.glyphicon-plus { + &:before { + content: "\2b"; + } +} + +.glyphicon-euro, +.glyphicon-eur { + &:before { + content: "\20ac"; + } +} + +.glyphicon-minus { + &:before { + content: "\2212"; + } +} + +.glyphicon-cloud { + &:before { + content: "\2601"; + } +} + +.glyphicon-envelope { + &:before { + content: "\2709"; + } +} + +.glyphicon-pencil { + &:before { + content: "\270f"; + } +} + +.glyphicon-glass { + &:before { + content: "\e001"; + } +} + +.glyphicon-music { + &:before { + content: "\e002"; + } +} + +.glyphicon-search { + &:before { + content: "\e003"; + } +} + +.glyphicon-heart { + &:before { + content: "\e005"; + } +} + +.glyphicon-star { + &:before { + content: "\e006"; + } +} + +.glyphicon-star-empty { + &:before { + content: "\e007"; + } +} + +.glyphicon-user { + &:before { + content: "\e008"; + } +} + +.glyphicon-film { + &:before { + content: "\e009"; + } +} + +.glyphicon-th-large { + &:before { + content: "\e010"; + } +} + +.glyphicon-th { + &:before { + content: "\e011"; + } +} + +.glyphicon-th-list { + &:before { + content: "\e012"; + } +} + +.glyphicon-ok { + &:before { + content: "\e013"; + } +} + +.glyphicon-remove { + &:before { + content: "\e014"; + } +} + +.glyphicon-zoom-in { + &:before { + content: "\e015"; + } +} + +.glyphicon-zoom-out { + &:before { + content: "\e016"; + } +} + +.glyphicon-off { + &:before { + content: "\e017"; + } +} + +.glyphicon-signal { + &:before { + content: "\e018"; + } +} + +.glyphicon-cog { + &:before { + content: "\e019"; + } +} + +.glyphicon-trash { + &:before { + content: "\e020"; + } +} + +.glyphicon-home { + &:before { + content: "\e021"; + } +} + +.glyphicon-file { + &:before { + content: "\e022"; + } +} + +.glyphicon-time { + &:before { + content: "\e023"; + } +} + +.glyphicon-road { + &:before { + content: "\e024"; + } +} + +.glyphicon-download-alt { + &:before { + content: "\e025"; + } +} + +.glyphicon-download { + &:before { + content: "\e026"; + } +} + +.glyphicon-upload { + &:before { + content: "\e027"; + } +} + +.glyphicon-inbox { + &:before { + content: "\e028"; + } +} + +.glyphicon-play-circle { + &:before { + content: "\e029"; + } +} + +.glyphicon-repeat { + &:before { + content: "\e030"; + } +} + +.glyphicon-refresh { + &:before { + content: "\e031"; + } +} + +.glyphicon-list-alt { + &:before { + content: "\e032"; + } +} + +.glyphicon-lock { + &:before { + content: "\e033"; + } +} + +.glyphicon-flag { + &:before { + content: "\e034"; + } +} + +.glyphicon-headphones { + &:before { + content: "\e035"; + } +} + +.glyphicon-volume-off { + &:before { + content: "\e036"; + } +} + +.glyphicon-volume-down { + &:before { + content: "\e037"; + } +} + +.glyphicon-volume-up { + &:before { + content: "\e038"; + } +} + +.glyphicon-qrcode { + &:before { + content: "\e039"; + } +} + +.glyphicon-barcode { + &:before { + content: "\e040"; + } +} + +.glyphicon-tag { + &:before { + content: "\e041"; + } +} + +.glyphicon-tags { + &:before { + content: "\e042"; + } +} + +.glyphicon-book { + &:before { + content: "\e043"; + } +} + +.glyphicon-bookmark { + &:before { + content: "\e044"; + } +} + +.glyphicon-print { + &:before { + content: "\e045"; + } +} + +.glyphicon-camera { + &:before { + content: "\e046"; + } +} + +.glyphicon-font { + &:before { + content: "\e047"; + } +} + +.glyphicon-bold { + &:before { + content: "\e048"; + } +} + +.glyphicon-italic { + &:before { + content: "\e049"; + } +} + +.glyphicon-text-height { + &:before { + content: "\e050"; + } +} + +.glyphicon-text-width { + &:before { + content: "\e051"; + } +} + +.glyphicon-align-left { + &:before { + content: "\e052"; + } +} + +.glyphicon-align-center { + &:before { + content: "\e053"; + } +} + +.glyphicon-align-right { + &:before { + content: "\e054"; + } +} + +.glyphicon-align-justify { + &:before { + content: "\e055"; + } +} + +.glyphicon-list { + &:before { + content: "\e056"; + } +} + +.glyphicon-indent-left { + &:before { + content: "\e057"; + } +} + +.glyphicon-indent-right { + &:before { + content: "\e058"; + } +} + +.glyphicon-facetime-video { + &:before { + content: "\e059"; + } +} + +.glyphicon-picture { + &:before { + content: "\e060"; + } +} + +.glyphicon-map-marker { + &:before { + content: "\e062"; + } +} + +.glyphicon-adjust { + &:before { + content: "\e063"; + } +} + +.glyphicon-tint { + &:before { + content: "\e064"; + } +} + +.glyphicon-edit { + &:before { + content: "\e065"; + } +} + +.glyphicon-share { + &:before { + content: "\e066"; + } +} + +.glyphicon-check { + &:before { + content: "\e067"; + } +} + +.glyphicon-move { + &:before { + content: "\e068"; + } +} + +.glyphicon-step-backward { + &:before { + content: "\e069"; + } +} + +.glyphicon-fast-backward { + &:before { + content: "\e070"; + } +} + +.glyphicon-backward { + &:before { + content: "\e071"; + } +} + +.glyphicon-play { + &:before { + content: "\e072"; + } +} + +.glyphicon-pause { + &:before { + content: "\e073"; + } +} + +.glyphicon-stop { + &:before { + content: "\e074"; + } +} + +.glyphicon-forward { + &:before { + content: "\e075"; + } +} + +.glyphicon-fast-forward { + &:before { + content: "\e076"; + } +} + +.glyphicon-step-forward { + &:before { + content: "\e077"; + } +} + +.glyphicon-eject { + &:before { + content: "\e078"; + } +} + +.glyphicon-chevron-left { + &:before { + content: "\e079"; + } +} + +.glyphicon-chevron-right { + &:before { + content: "\e080"; + } +} + +.glyphicon-plus-sign { + &:before { + content: "\e081"; + } +} + +.glyphicon-minus-sign { + &:before { + content: "\e082"; + } +} + +.glyphicon-remove-sign { + &:before { + content: "\e083"; + } +} + +.glyphicon-ok-sign { + &:before { + content: "\e084"; + } +} + +.glyphicon-question-sign { + &:before { + content: "\e085"; + } +} + +.glyphicon-info-sign { + &:before { + content: "\e086"; + } +} + +.glyphicon-screenshot { + &:before { + content: "\e087"; + } +} + +.glyphicon-remove-circle { + &:before { + content: "\e088"; + } +} + +.glyphicon-ok-circle { + &:before { + content: "\e089"; + } +} + +.glyphicon-ban-circle { + &:before { + content: "\e090"; + } +} + +.glyphicon-arrow-left { + &:before { + content: "\e091"; + } +} + +.glyphicon-arrow-right { + &:before { + content: "\e092"; + } +} + +.glyphicon-arrow-up { + &:before { + content: "\e093"; + } +} + +.glyphicon-arrow-down { + &:before { + content: "\e094"; + } +} + +.glyphicon-share-alt { + &:before { + content: "\e095"; + } +} + +.glyphicon-resize-full { + &:before { + content: "\e096"; + } +} + +.glyphicon-resize-small { + &:before { + content: "\e097"; + } +} + +.glyphicon-exclamation-sign { + &:before { + content: "\e101"; + } +} + +.glyphicon-gift { + &:before { + content: "\e102"; + } +} + +.glyphicon-leaf { + &:before { + content: "\e103"; + } +} + +.glyphicon-fire { + &:before { + content: "\e104"; + } +} + +.glyphicon-eye-open { + &:before { + content: "\e105"; + } +} + +.glyphicon-eye-close { + &:before { + content: "\e106"; + } +} + +.glyphicon-warning-sign { + &:before { + content: "\e107"; + } +} + +.glyphicon-plane { + &:before { + content: "\e108"; + } +} + +.glyphicon-calendar { + &:before { + content: "\e109"; + } +} + +.glyphicon-random { + &:before { + content: "\e110"; + } +} + +.glyphicon-comment { + &:before { + content: "\e111"; + } +} + +.glyphicon-magnet { + &:before { + content: "\e112"; + } +} + +.glyphicon-chevron-up { + &:before { + content: "\e113"; + } +} + +.glyphicon-chevron-down { + &:before { + content: "\e114"; + } +} + +.glyphicon-retweet { + &:before { + content: "\e115"; + } +} + +.glyphicon-shopping-cart { + &:before { + content: "\e116"; + } +} + +.glyphicon-folder-close { + &:before { + content: "\e117"; + } +} + +.glyphicon-folder-open { + &:before { + content: "\e118"; + } +} + +.glyphicon-resize-vertical { + &:before { + content: "\e119"; + } +} + +.glyphicon-resize-horizontal { + &:before { + content: "\e120"; + } +} + +.glyphicon-hdd { + &:before { + content: "\e121"; + } +} + +.glyphicon-bullhorn { + &:before { + content: "\e122"; + } +} + +.glyphicon-bell { + &:before { + content: "\e123"; + } +} + +.glyphicon-certificate { + &:before { + content: "\e124"; + } +} + +.glyphicon-thumbs-up { + &:before { + content: "\e125"; + } +} + +.glyphicon-thumbs-down { + &:before { + content: "\e126"; + } +} + +.glyphicon-hand-right { + &:before { + content: "\e127"; + } +} + +.glyphicon-hand-left { + &:before { + content: "\e128"; + } +} + +.glyphicon-hand-up { + &:before { + content: "\e129"; + } +} + +.glyphicon-hand-down { + &:before { + content: "\e130"; + } +} + +.glyphicon-circle-arrow-right { + &:before { + content: "\e131"; + } +} + +.glyphicon-circle-arrow-left { + &:before { + content: "\e132"; + } +} + +.glyphicon-circle-arrow-up { + &:before { + content: "\e133"; + } +} + +.glyphicon-circle-arrow-down { + &:before { + content: "\e134"; + } +} + +.glyphicon-globe { + &:before { + content: "\e135"; + } +} + +.glyphicon-wrench { + &:before { + content: "\e136"; + } +} + +.glyphicon-tasks { + &:before { + content: "\e137"; + } +} + +.glyphicon-filter { + &:before { + content: "\e138"; + } +} + +.glyphicon-briefcase { + &:before { + content: "\e139"; + } +} + +.glyphicon-fullscreen { + &:before { + content: "\e140"; + } +} + +.glyphicon-dashboard { + &:before { + content: "\e141"; + } +} + +.glyphicon-paperclip { + &:before { + content: "\e142"; + } +} + +.glyphicon-heart-empty { + &:before { + content: "\e143"; + } +} + +.glyphicon-link { + &:before { + content: "\e144"; + } +} + +.glyphicon-phone { + &:before { + content: "\e145"; + } +} + +.glyphicon-pushpin { + &:before { + content: "\e146"; + } +} + +.glyphicon-usd { + &:before { + content: "\e148"; + } +} + +.glyphicon-gbp { + &:before { + content: "\e149"; + } +} + +.glyphicon-sort { + &:before { + content: "\e150"; + } +} + +.glyphicon-sort-by-alphabet { + &:before { + content: "\e151"; + } +} + +.glyphicon-sort-by-alphabet-alt { + &:before { + content: "\e152"; + } +} + +.glyphicon-sort-by-order { + &:before { + content: "\e153"; + } +} + +.glyphicon-sort-by-order-alt { + &:before { + content: "\e154"; + } +} + +.glyphicon-sort-by-attributes { + &:before { + content: "\e155"; + } +} + +.glyphicon-sort-by-attributes-alt { + &:before { + content: "\e156"; + } +} + +.glyphicon-unchecked { + &:before { + content: "\e157"; + } +} + +.glyphicon-expand { + &:before { + content: "\e158"; + } +} + +.glyphicon-collapse-down { + &:before { + content: "\e159"; + } +} + +.glyphicon-collapse-up { + &:before { + content: "\e160"; + } +} + +.glyphicon-log-in { + &:before { + content: "\e161"; + } +} + +.glyphicon-flash { + &:before { + content: "\e162"; + } +} + +.glyphicon-log-out { + &:before { + content: "\e163"; + } +} + +.glyphicon-new-window { + &:before { + content: "\e164"; + } +} + +.glyphicon-record { + &:before { + content: "\e165"; + } +} + +.glyphicon-save { + &:before { + content: "\e166"; + } +} + +.glyphicon-open { + &:before { + content: "\e167"; + } +} + +.glyphicon-saved { + &:before { + content: "\e168"; + } +} + +.glyphicon-import { + &:before { + content: "\e169"; + } +} + +.glyphicon-export { + &:before { + content: "\e170"; + } +} + +.glyphicon-send { + &:before { + content: "\e171"; + } +} + +.glyphicon-floppy-disk { + &:before { + content: "\e172"; + } +} + +.glyphicon-floppy-saved { + &:before { + content: "\e173"; + } +} + +.glyphicon-floppy-remove { + &:before { + content: "\e174"; + } +} + +.glyphicon-floppy-save { + &:before { + content: "\e175"; + } +} + +.glyphicon-floppy-open { + &:before { + content: "\e176"; + } +} + +.glyphicon-credit-card { + &:before { + content: "\e177"; + } +} + +.glyphicon-transfer { + &:before { + content: "\e178"; + } +} + +.glyphicon-cutlery { + &:before { + content: "\e179"; + } +} + +.glyphicon-header { + &:before { + content: "\e180"; + } +} + +.glyphicon-compressed { + &:before { + content: "\e181"; + } +} + +.glyphicon-earphone { + &:before { + content: "\e182"; + } +} + +.glyphicon-phone-alt { + &:before { + content: "\e183"; + } +} + +.glyphicon-tower { + &:before { + content: "\e184"; + } +} + +.glyphicon-stats { + &:before { + content: "\e185"; + } +} + +.glyphicon-sd-video { + &:before { + content: "\e186"; + } +} + +.glyphicon-hd-video { + &:before { + content: "\e187"; + } +} + +.glyphicon-subtitles { + &:before { + content: "\e188"; + } +} + +.glyphicon-sound-stereo { + &:before { + content: "\e189"; + } +} + +.glyphicon-sound-dolby { + &:before { + content: "\e190"; + } +} + +.glyphicon-sound-5-1 { + &:before { + content: "\e191"; + } +} + +.glyphicon-sound-6-1 { + &:before { + content: "\e192"; + } +} + +.glyphicon-sound-7-1 { + &:before { + content: "\e193"; + } +} + +.glyphicon-copyright-mark { + &:before { + content: "\e194"; + } +} + +.glyphicon-registration-mark { + &:before { + content: "\e195"; + } +} + +.glyphicon-cloud-download { + &:before { + content: "\e197"; + } +} + +.glyphicon-cloud-upload { + &:before { + content: "\e198"; + } +} + +.glyphicon-tree-conifer { + &:before { + content: "\e199"; + } +} + +.glyphicon-tree-deciduous { + &:before { + content: "\e200"; + } +} + +.glyphicon-cd { + &:before { + content: "\e201"; + } +} + +.glyphicon-save-file { + &:before { + content: "\e202"; + } +} + +.glyphicon-open-file { + &:before { + content: "\e203"; + } +} + +.glyphicon-level-up { + &:before { + content: "\e204"; + } +} + +.glyphicon-copy { + &:before { + content: "\e205"; + } +} + +.glyphicon-paste { + &:before { + content: "\e206"; + } +} + +// The following 2 Glyphicons are omitted for the time being because +// they currently use Unicode codepoints that are outside the +// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle +// non-BMP codepoints in CSS string escapes, and thus can't display these two icons. +// Notably, the bug affects some older versions of the Android Browser. +// More info: https://github.com/twbs/bootstrap/issues/10106 +// .glyphicon-door { &:before { content: "\1f6aa"; } } +// .glyphicon-key { &:before { content: "\1f511"; } } +.glyphicon-alert { + &:before { + content: "\e209"; + } +} + +.glyphicon-equalizer { + &:before { + content: "\e210"; + } +} + +.glyphicon-king { + &:before { + content: "\e211"; + } +} + +.glyphicon-queen { + &:before { + content: "\e212"; + } +} + +.glyphicon-pawn { + &:before { + content: "\e213"; + } +} + +.glyphicon-bishop { + &:before { + content: "\e214"; + } +} + +.glyphicon-knight { + &:before { + content: "\e215"; + } +} + +.glyphicon-baby-formula { + &:before { + content: "\e216"; + } +} + +.glyphicon-tent { + &:before { + content: "\26fa"; + } +} + +.glyphicon-blackboard { + &:before { + content: "\e218"; + } +} + +.glyphicon-bed { + &:before { + content: "\e219"; + } +} + +.glyphicon-apple { + &:before { + content: "\f8ff"; + } +} + +.glyphicon-erase { + &:before { + content: "\e221"; + } +} + +.glyphicon-hourglass { + &:before { + content: "\231b"; + } +} + +.glyphicon-lamp { + &:before { + content: "\e223"; + } +} + +.glyphicon-duplicate { + &:before { + content: "\e224"; + } +} + +.glyphicon-piggy-bank { + &:before { + content: "\e225"; + } +} + +.glyphicon-scissors { + &:before { + content: "\e226"; + } +} + +.glyphicon-bitcoin { + &:before { + content: "\e227"; + } +} + +.glyphicon-btc { + &:before { + content: "\e227"; + } +} + +.glyphicon-xbt { + &:before { + content: "\e227"; + } +} + +.glyphicon-yen { + &:before { + content: "\00a5"; + } +} + +.glyphicon-jpy { + &:before { + content: "\00a5"; + } +} + +.glyphicon-ruble { + &:before { + content: "\20bd"; + } +} + +.glyphicon-rub { + &:before { + content: "\20bd"; + } +} + +.glyphicon-scale { + &:before { + content: "\e230"; + } +} + +.glyphicon-ice-lolly { + &:before { + content: "\e231"; + } +} + +.glyphicon-ice-lolly-tasted { + &:before { + content: "\e232"; + } +} + +.glyphicon-education { + &:before { + content: "\e233"; + } +} + +.glyphicon-option-horizontal { + &:before { + content: "\e234"; + } +} + +.glyphicon-option-vertical { + &:before { + content: "\e235"; + } +} + +.glyphicon-menu-hamburger { + &:before { + content: "\e236"; + } +} + +.glyphicon-modal-window { + &:before { + content: "\e237"; + } +} + +.glyphicon-oil { + &:before { + content: "\e238"; + } +} + +.glyphicon-grain { + &:before { + content: "\e239"; + } +} + +.glyphicon-sunglasses { + &:before { + content: "\e240"; + } +} + +.glyphicon-text-size { + &:before { + content: "\e241"; + } +} + +.glyphicon-text-color { + &:before { + content: "\e242"; + } +} + +.glyphicon-text-background { + &:before { + content: "\e243"; + } +} + +.glyphicon-object-align-top { + &:before { + content: "\e244"; + } +} + +.glyphicon-object-align-bottom { + &:before { + content: "\e245"; + } +} + +.glyphicon-object-align-horizontal { + &:before { + content: "\e246"; + } +} + +.glyphicon-object-align-left { + &:before { + content: "\e247"; + } +} + +.glyphicon-object-align-vertical { + &:before { + content: "\e248"; + } +} + +.glyphicon-object-align-right { + &:before { + content: "\e249"; + } +} + +.glyphicon-triangle-right { + &:before { + content: "\e250"; + } +} + +.glyphicon-triangle-left { + &:before { + content: "\e251"; + } +} + +.glyphicon-triangle-bottom { + &:before { + content: "\e252"; + } +} + +.glyphicon-triangle-top { + &:before { + content: "\e253"; + } +} + +.glyphicon-console { + &:before { + content: "\e254"; + } +} + +.glyphicon-superscript { + &:before { + content: "\e255"; + } +} + +.glyphicon-subscript { + &:before { + content: "\e256"; + } +} + +.glyphicon-menu-left { + &:before { + content: "\e257"; + } +} + +.glyphicon-menu-right { + &:before { + content: "\e258"; + } +} + +.glyphicon-menu-down { + &:before { + content: "\e259"; + } +} + +.glyphicon-menu-up { + &:before { + content: "\e260"; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_grid.scss b/openecomp-ui/resources/scss/bootstrap/_grid.scss new file mode 100644 index 0000000000..75f3822540 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_grid.scss @@ -0,0 +1,76 @@ +// +// Grid system +// -------------------------------------------------- + +// Container widths +// +// Set the container width, and override it for fixed navbars in media queries. + +.container { + @include container-fixed; + + @media (min-width: $screen-sm-min) { + width: $container-sm; + } + @media (min-width: $screen-md-min) { + width: $container-md; + } + @media (min-width: $screen-lg-min) { + width: $container-lg; + } +} + +// Fluid container +// +// Utilizes the mixin meant for fixed width containers, but without any defined +// width for fluid, full width layouts. + +.container-fluid { + @include container-fixed; +} + +// Row +// +// Rows contain and clear the floats of your columns. + +.row { + @include make-row; +} + +// Columns +// +// Common styles for small and large grid columns + +@include make-grid-columns; + +// Extra small grid +// +// Columns, offsets, pushes, and pulls for extra small devices like +// smartphones. + +@include make-grid(xs); + +// Small grid +// +// Columns, offsets, pushes, and pulls for the small device range, from phones +// to tablets. + +@media (min-width: $screen-sm-min) { + @include make-grid(sm); +} + +// Medium grid +// +// Columns, offsets, pushes, and pulls for the desktop device range. + +@media (min-width: $screen-md-min) { + @include make-grid(md); +} + +// Large grid +// +// Columns, offsets, pushes, and pulls for the large desktop device range. + +@media (min-width: $screen-lg-min) { + @include make-grid(lg); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_input-groups.scss b/openecomp-ui/resources/scss/bootstrap/_input-groups.scss new file mode 100644 index 0000000000..4a160e0787 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_input-groups.scss @@ -0,0 +1,171 @@ +// +// Input groups +// -------------------------------------------------- + +// Base styles +// ------------------------- +.input-group { + position: relative; // For dropdowns + display: table; + border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table + + // Undo padding and float of grid classes + &[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; + } + + .form-control { + // Ensure that the input is always above the *appended* addon button for + // proper border colors. + position: relative; + z-index: 2; + + // IE9 fubars the placeholder attribute in text inputs and the arrows on + // select elements in input groups. To fix it, we float the input. Details: + // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855 + float: left; + + width: 100%; + margin-bottom: 0; + } +} + +// Sizing options +// +// Remix the default form control sizing classes into new ones for easier +// manipulation. + +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + @extend .input-lg; +} + +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + @extend .input-sm; +} + +// Display as table-cell +// ------------------------- +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; + + &:not(:first-child):not(:last-child) { + border-radius: 0; + } +} + +// Addon and addon wrapper for buttons +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; // Match the inputs +} + +// Text input groups +// ------------------------- +.input-group-addon { + padding: $padding-base-vertical $padding-base-horizontal; + font-size: $font-size-base; + font-weight: normal; + line-height: 1; + color: $input-color; + text-align: center; + background-color: $input-group-addon-bg; + border: 1px solid $input-group-addon-border-color; + border-radius: $border-radius-base; + + // Sizing + &.input-sm { + padding: $padding-small-vertical $padding-small-horizontal; + font-size: $font-size-small; + border-radius: $border-radius-small; + } + &.input-lg { + padding: $padding-large-vertical $padding-large-horizontal; + font-size: $font-size-large; + border-radius: $border-radius-large; + } + + // Nuke default margins from checkboxes and radios to vertically center within. + input[type="radio"], + input[type="checkbox"] { + margin-top: 0; + } +} + +// Reset rounded corners +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + @include border-right-radius(0); +} + +.input-group-addon:first-child { + border-right: 0; +} + +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + @include border-left-radius(0); +} + +.input-group-addon:last-child { + border-left: 0; +} + +// Button input groups +// ------------------------- +.input-group-btn { + position: relative; + // Jankily prevent input button groups from wrapping with `white-space` and + // `font-size` in combination with `inline-block` on buttons. + font-size: 0; + white-space: nowrap; + + // Negative margin for spacing, position for bringing hovered/focused/actived + // element above the siblings. + > .btn { + position: relative; + + .btn { + margin-left: -1px; + } + // Bring the "active" button to the front + &:hover, + &:focus, + &:active { + z-index: 2; + } + } + + // Negative margin to only have a 1px border between the two + &:first-child { + > .btn, + > .btn-group { + margin-right: -1px; + } + } + &:last-child { + > .btn, + > .btn-group { + z-index: 2; + margin-left: -1px; + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_jumbotron.scss b/openecomp-ui/resources/scss/bootstrap/_jumbotron.scss new file mode 100644 index 0000000000..e31137137d --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_jumbotron.scss @@ -0,0 +1,51 @@ +// +// Jumbotron +// -------------------------------------------------- + +.jumbotron { + padding-top: $jumbotron-padding; + padding-bottom: $jumbotron-padding; + margin-bottom: $jumbotron-padding; + color: $jumbotron-color; + background-color: $jumbotron-bg; + + h1, + .h1 { + color: $jumbotron-heading-color; + } + + p { + margin-bottom: ($jumbotron-padding / 2); + font-size: $jumbotron-font-size; + font-weight: 200; + } + + > hr { + border-top-color: darken($jumbotron-bg, 10%); + } + + .container &, + .container-fluid & { + border-radius: $border-radius-large; // Only round corners at higher resolutions if contained in a container + } + + .container { + max-width: 100%; + } + + @media screen and (min-width: $screen-sm-min) { + padding-top: ($jumbotron-padding * 1.6); + padding-bottom: ($jumbotron-padding * 1.6); + + .container &, + .container-fluid & { + padding-left: ($jumbotron-padding * 2); + padding-right: ($jumbotron-padding * 2); + } + + h1, + .h1 { + font-size: $jumbotron-heading-font-size; + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_labels.scss b/openecomp-ui/resources/scss/bootstrap/_labels.scss new file mode 100644 index 0000000000..4fea19d99a --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_labels.scss @@ -0,0 +1,66 @@ +// +// Labels +// -------------------------------------------------- + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: $label-color; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; + + // [converter] extracted a& to a.label + + // Empty labels collapse automatically (not available in IE8) + &:empty { + display: none; + } + + // Quick fix for labels in buttons + .btn & { + position: relative; + top: -1px; + } +} + +// Add hover effects, but only for links +a.label { + &:hover, + &:focus { + color: $label-link-hover-color; + text-decoration: none; + cursor: pointer; + } +} + +// Colors +// Contextual variations (linked labels get darker on :hover) + +.label-default { + @include label-variant($label-default-bg); +} + +.label-primary { + @include label-variant($label-primary-bg); +} + +.label-success { + @include label-variant($label-success-bg); +} + +.label-info { + @include label-variant($label-info-bg); +} + +.label-warning { + @include label-variant($label-warning-bg); +} + +.label-danger { + @include label-variant($label-danger-bg); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_list-group.scss b/openecomp-ui/resources/scss/bootstrap/_list-group.scss new file mode 100644 index 0000000000..d04b72ee45 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_list-group.scss @@ -0,0 +1,126 @@ +// +// List groups +// -------------------------------------------------- + +// Base class +// +// Easily usable on
    ,
      , or
      . + +.list-group { + // No need to set list-style: none; since .list-group-item is block level + margin-bottom: 20px; + padding-left: 0; // reset padding because ul and ol +} + +// Individual list items +// +// Use on `li`s or `div`s within the `.list-group` parent. + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + // Place the border on the list items and negative margin up for better styling + margin-bottom: -1px; + background-color: $list-group-bg; + border: 1px solid $list-group-border; + + // Round the first and last items + &:first-child { + @include border-top-radius($list-group-border-radius); + } + &:last-child { + margin-bottom: 0; + @include border-bottom-radius($list-group-border-radius); + } +} + +// Interactive list items +// +// Use anchor or button elements instead of `li`s or `div`s to create interactive items. +// Includes an extra `.active` modifier class for showing selected items. + +a.list-group-item, +button.list-group-item { + color: $list-group-link-color; + + .list-group-item-heading { + color: $list-group-link-heading-color; + } + + // Hover state + &:hover, + &:focus { + text-decoration: none; + color: $list-group-link-hover-color; + background-color: $list-group-hover-bg; + } +} + +button.list-group-item { + width: 100%; + text-align: left; +} + +.list-group-item { + // Disabled state + &.disabled, + &.disabled:hover, + &.disabled:focus { + background-color: $list-group-disabled-bg; + color: $list-group-disabled-color; + cursor: $cursor-disabled; + + // Force color to inherit for custom content + .list-group-item-heading { + color: inherit; + } + .list-group-item-text { + color: $list-group-disabled-text-color; + } + } + + // Active class on item itself, not parent + &.active, + &.active:hover, + &.active:focus { + z-index: 2; // Place active items above their siblings for proper border styling + color: $list-group-active-color; + background-color: $list-group-active-bg; + border-color: $list-group-active-border; + + // Force color to inherit for custom content + .list-group-item-heading, + .list-group-item-heading > small, + .list-group-item-heading > .small { + color: inherit; + } + .list-group-item-text { + color: $list-group-active-text-color; + } + } +} + +// Contextual variants +// +// Add modifier classes to change text and background color on individual items. +// Organizationally, this must come after the `:hover` states. + +@include list-group-item-variant(success, $state-success-bg, $state-success-text); +@include list-group-item-variant(info, $state-info-bg, $state-info-text); +@include list-group-item-variant(warning, $state-warning-bg, $state-warning-text); +@include list-group-item-variant(danger, $state-danger-bg, $state-danger-text); + +// Custom content options +// +// Extra classes for creating well-formatted content within `.list-group-item`s. + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_media.scss b/openecomp-ui/resources/scss/bootstrap/_media.scss new file mode 100644 index 0000000000..b9a5663ee8 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_media.scss @@ -0,0 +1,66 @@ +.media { + // Proper spacing between instances of .media + margin-top: 15px; + + &:first-child { + margin-top: 0; + } +} + +.media, +.media-body { + zoom: 1; + overflow: hidden; +} + +.media-body { + width: 10000px; +} + +.media-object { + display: block; + + // Fix collapse in webkit from max-width: 100% and display: table-cell. + &.img-thumbnail { + max-width: none; + } +} + +.media-right, +.media > .pull-right { + padding-left: 10px; +} + +.media-left, +.media > .pull-left { + padding-right: 10px; +} + +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} + +.media-middle { + vertical-align: middle; +} + +.media-bottom { + vertical-align: bottom; +} + +// Reset margins on headings for tighter default spacing +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} + +// Media list variation +// +// Undo default ul/ol styles +.media-list { + padding-left: 0; + list-style: none; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_mixins.scss b/openecomp-ui/resources/scss/bootstrap/_mixins.scss new file mode 100644 index 0000000000..62dfda69dc --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_mixins.scss @@ -0,0 +1,36 @@ +// Mixins +// -------------------------------------------------- +// Utilities +@import "mixins/hide-text"; +@import "mixins/opacity"; +@import "mixins/image"; +@import "mixins/labels"; +@import "mixins/reset-filter"; +@import "mixins/resize"; +@import "mixins/responsive-visibility"; +@import "mixins/size"; +@import "mixins/tab-focus"; +@import "mixins/reset-text"; +@import "mixins/text-emphasis"; +@import "mixins/text-overflow"; +@import "mixins/vendor-prefixes"; +// Components +@import "mixins/alerts"; +@import "mixins/buttons"; +@import "mixins/panels"; +@import "mixins/pagination"; +@import "mixins/list-group"; +@import "mixins/nav-divider"; +@import "mixins/forms"; +@import "mixins/progress-bar"; +@import "mixins/table-row"; +// Skins +@import "mixins/background-variant"; +@import "mixins/border-radius"; +@import "mixins/gradients"; +// Layout +@import "mixins/clearfix"; +@import "mixins/center-block"; +@import "mixins/nav-vertical-align"; +@import "mixins/grid-framework"; +@import "mixins/grid"; diff --git a/openecomp-ui/resources/scss/bootstrap/_modals.scss b/openecomp-ui/resources/scss/bootstrap/_modals.scss new file mode 100644 index 0000000000..e089e7bfae --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_modals.scss @@ -0,0 +1,162 @@ +// +// Modals +// -------------------------------------------------- + +// .modal-open - body class for killing the scroll +// .modal - container to scroll within +// .modal-dialog - positioning shell for the actual modal +// .modal-content - actual modal w/ bg and corners and shit + +// Kill the scroll on the body +.modal-open { + overflow: hidden; +} + +// Container that the modal scrolls within +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: $zindex-modal; + -webkit-overflow-scrolling: touch; + + // Prevent Chrome on Windows from adding a focus outline. For details, see + // https://github.com/twbs/bootstrap/pull/10951. + outline: 0; + + // When fading in the modal, animate it to slide down + &.fade .modal-dialog { + @include translate(0, -25%); + @include transition-transform(0.3s ease-out); + } + &.in .modal-dialog { + @include translate(0, 0) + } +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +// Shell div to position the modal with bottom padding +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} + +// Actual modal +.modal-content { + position: relative; + background-color: $modal-content-bg; + border: 1px solid $modal-content-fallback-border-color; //old browsers fallback (ie8 etc) + border: 1px solid $modal-content-border-color; + border-radius: $border-radius-large; + @include box-shadow(0 3px 9px rgba(0, 0, 0, .5)); + background-clip: padding-box; + // Remove focus outline from opened modal + outline: 0; +} + +// Modal background +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: $zindex-modal-background; + background-color: $modal-backdrop-bg; + // Fade for backdrop + &.fade { + @include opacity(0); + } + &.in { + @include opacity($modal-backdrop-opacity); + } +} + +// Modal header +// Top section of the modal w/ title and dismiss +.modal-header { + padding: $modal-title-padding; + border-bottom: 1px solid $modal-header-border-color; + min-height: ($modal-title-padding + $modal-title-line-height); +} + +// Close icon +.modal-header .close { + margin-top: -2px; +} + +// Title text within header +.modal-title { + margin: 0; + line-height: $modal-title-line-height; +} + +// Modal body +// Where all modal content resides (sibling of .modal-header and .modal-footer) +.modal-body { + position: relative; + padding: $modal-inner-padding; +} + +// Footer (for actions) +.modal-footer { + padding: $modal-inner-padding; + text-align: right; // right align buttons + border-top: 1px solid $modal-footer-border-color; + @include clearfix; // clear it in case folks use .pull-* classes on buttons + + // Properly space out buttons + .btn + .btn { + margin-left: 5px; + margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs + } + // but override that for button groups + .btn-group .btn + .btn { + margin-left: -1px; + } + // and override it for block buttons as well + .btn-block + .btn-block { + margin-left: 0; + } +} + +// Measure scrollbar width for padding body during modal show/hide +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +// Scale up the modal +@media (min-width: $screen-sm-min) { + // Automatically set modal's width for larger viewports + .modal-dialog { + width: $modal-md; + margin: 30px auto; + } + .modal-content { + @include box-shadow(0 5px 15px rgba(0, 0, 0, .5)); + } + + // Modal sizes + .modal-sm { + width: $modal-sm; + } +} + +@media (min-width: $screen-md-min) { + .modal-lg { + width: $modal-lg; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_navbar.scss b/openecomp-ui/resources/scss/bootstrap/_navbar.scss new file mode 100644 index 0000000000..af38fd9e97 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_navbar.scss @@ -0,0 +1,650 @@ +// +// Navbars +// -------------------------------------------------- + +// Wrapper and base class +// +// Provide a static navbar from which we expand to create full-width, fixed, and +// other navbar variations. + +.navbar { + position: relative; + min-height: $navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode) + margin-bottom: $navbar-margin-bottom; + border: 1px solid transparent; + + // Prevent floats from breaking the navbar + @include clearfix; + + @media (min-width: $grid-float-breakpoint) { + border-radius: $navbar-border-radius; + } +} + +// Navbar heading +// +// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy +// styling of responsive aspects. + +.navbar-header { + @include clearfix; + + @media (min-width: $grid-float-breakpoint) { + float: left; + } +} + +// Navbar collapse (body) +// +// Group your navbar content into this for easy collapsing and expanding across +// various device sizes. By default, this content is collapsed when <768px, but +// will expand past that for a horizontal display. +// +// To start (on mobile devices) the navbar links, forms, and buttons are stacked +// vertically and include a `max-height` to overflow in case you have too much +// content for the user's viewport. + +.navbar-collapse { + overflow-x: visible; + padding-right: $navbar-padding-horizontal; + padding-left: $navbar-padding-horizontal; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + @include clearfix; + -webkit-overflow-scrolling: touch; + + &.in { + overflow-y: auto; + } + + @media (min-width: $grid-float-breakpoint) { + width: auto; + border-top: 0; + box-shadow: none; + + &.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; // Override default setting + overflow: visible !important; + } + + &.in { + overflow-y: visible; + } + + // Undo the collapse side padding for navbars with containers to ensure + // alignment of right-aligned contents. + .navbar-fixed-top &, + .navbar-static-top &, + .navbar-fixed-bottom & { + padding-left: 0; + padding-right: 0; + } + } +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + .navbar-collapse { + max-height: $navbar-collapse-max-height; + + @media (max-device-width: $screen-xs-min) and (orientation: landscape) { + max-height: 200px; + } + } +} + +// Both navbar header and collapse +// +// When a container is present, change the behavior of the header and collapse. + +.container, +.container-fluid { + > .navbar-header, + > .navbar-collapse { + margin-right: -$navbar-padding-horizontal; + margin-left: -$navbar-padding-horizontal; + + @media (min-width: $grid-float-breakpoint) { + margin-right: 0; + margin-left: 0; + } + } +} + +// +// Navbar alignment options +// +// Display the navbar across the entirety of the page or fixed it to the top or +// bottom of the page. + +// Static top (unfixed, but 100% wide) navbar +.navbar-static-top { + z-index: $zindex-navbar; + border-width: 0 0 1px; + + @media (min-width: $grid-float-breakpoint) { + border-radius: 0; + } +} + +// Fix the top/bottom navbars when screen real estate supports it +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: $zindex-navbar-fixed; + + // Undo the rounded corners + @media (min-width: $grid-float-breakpoint) { + border-radius: 0; + } +} + +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; // override .navbar defaults + border-width: 1px 0 0; +} + +// Brand/project name + +.navbar-brand { + float: left; + padding: $navbar-padding-vertical $navbar-padding-horizontal; + font-size: $font-size-large; + line-height: $line-height-computed; + height: $navbar-height; + + &:hover, + &:focus { + text-decoration: none; + } + + > img { + display: block; + } + + @media (min-width: $grid-float-breakpoint) { + .navbar > .container &, + .navbar > .container-fluid & { + margin-left: -$navbar-padding-horizontal; + } + } +} + +// Navbar toggle +// +// Custom button for toggling the `.navbar-collapse`, powered by the collapse +// JavaScript plugin. + +.navbar-toggle { + position: relative; + float: right; + margin-right: $navbar-padding-horizontal; + padding: 9px 10px; + @include navbar-vertical-align(34px); + background-color: transparent; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + border-radius: $border-radius-base; + + // We remove the `outline` here, but later compensate by attaching `:hover` + // styles to `:focus`. + &:focus { + outline: 0; + } + + // Bars + .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; + } + .icon-bar + .icon-bar { + margin-top: 4px; + } + + @media (min-width: $grid-float-breakpoint) { + display: none; + } +} + +// Navbar nav links +// +// Builds on top of the `.nav` components with its own modifier class to make +// the nav the full height of the horizontal nav (above 768px). + +.navbar-nav { + margin: ($navbar-padding-vertical / 2) (-$navbar-padding-horizontal); + + > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: $line-height-computed; + } + + @media (max-width: $grid-float-breakpoint-max) { + // Dropdowns get custom display when collapsed + .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + > li > a, + .dropdown-header { + padding: 5px 15px 5px 25px; + } + > li > a { + line-height: $line-height-computed; + &:hover, + &:focus { + background-image: none; + } + } + } + } + + // Uncollapse the nav + @media (min-width: $grid-float-breakpoint) { + float: left; + margin: 0; + + > li { + float: left; + > a { + padding-top: $navbar-padding-vertical; + padding-bottom: $navbar-padding-vertical; + } + } + } +} + +// Navbar form +// +// Extension of the `.form-inline` with some extra flavor for optimum display in +// our navbars. + +.navbar-form { + margin-left: -$navbar-padding-horizontal; + margin-right: -$navbar-padding-horizontal; + padding: 10px $navbar-padding-horizontal; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + $shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + @include box-shadow($shadow); + + // Mixin behavior for optimum display + @include form-inline; + + .form-group { + @media (max-width: $grid-float-breakpoint-max) { + margin-bottom: 5px; + + &:last-child { + margin-bottom: 0; + } + } + } + + // Vertically center in expanded, horizontal navbar + @include navbar-vertical-align($input-height-base); + + // Undo 100% width for pull classes + @media (min-width: $grid-float-breakpoint) { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + @include box-shadow(none); + } +} + +// Dropdown menus + +// Menu position and menu carets +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + @include border-top-radius(0); +} + +// Menu position and menu caret support for dropups via extra dropup class +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + @include border-top-radius($navbar-border-radius); + @include border-bottom-radius(0); +} + +// Buttons in navbars +// +// Vertically center a button within a navbar (when *not* in a form). + +.navbar-btn { + @include navbar-vertical-align($input-height-base); + + &.btn-sm { + @include navbar-vertical-align($input-height-small); + } + &.btn-xs { + @include navbar-vertical-align(22); + } +} + +// Text in navbars +// +// Add a class to make any element properly align itself vertically within the navbars. + +.navbar-text { + @include navbar-vertical-align($line-height-computed); + + @media (min-width: $grid-float-breakpoint) { + float: left; + margin-left: $navbar-padding-horizontal; + margin-right: $navbar-padding-horizontal; + } +} + +// Component alignment +// +// Repurpose the pull utilities as their own navbar utilities to avoid specificity +// issues with parents and chaining. Only do this when the navbar is uncollapsed +// though so that navbar contents properly stack and align in mobile. +// +// Declared after the navbar components to ensure more specificity on the margins. + +@media (min-width: $grid-float-breakpoint) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -$navbar-padding-horizontal; + + ~ .navbar-right { + margin-right: 0; + } + } +} + +// Alternate navbars +// -------------------------------------------------- + +// Default navbar +.navbar-default { + background-color: $navbar-default-bg; + border-color: $navbar-default-border; + + .navbar-brand { + color: $navbar-default-brand-color; + &:hover, + &:focus { + color: $navbar-default-brand-hover-color; + background-color: $navbar-default-brand-hover-bg; + } + } + + .navbar-text { + color: $navbar-default-color; + } + + .navbar-nav { + > li > a { + color: $navbar-default-link-color; + + &:hover, + &:focus { + color: $navbar-default-link-hover-color; + background-color: $navbar-default-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: $navbar-default-link-active-color; + background-color: $navbar-default-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: $navbar-default-link-disabled-color; + background-color: $navbar-default-link-disabled-bg; + } + } + } + + .navbar-toggle { + border-color: $navbar-default-toggle-border-color; + &:hover, + &:focus { + background-color: $navbar-default-toggle-hover-bg; + } + .icon-bar { + background-color: $navbar-default-toggle-icon-bar-bg; + } + } + + .navbar-collapse, + .navbar-form { + border-color: $navbar-default-border; + } + + // Dropdown menu items + .navbar-nav { + // Remove background color from open dropdown + > .open > a { + &, + &:hover, + &:focus { + background-color: $navbar-default-link-active-bg; + color: $navbar-default-link-active-color; + } + } + + @media (max-width: $grid-float-breakpoint-max) { + // Dropdowns get custom display when collapsed + .open .dropdown-menu { + > li > a { + color: $navbar-default-link-color; + &:hover, + &:focus { + color: $navbar-default-link-hover-color; + background-color: $navbar-default-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: $navbar-default-link-active-color; + background-color: $navbar-default-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: $navbar-default-link-disabled-color; + background-color: $navbar-default-link-disabled-bg; + } + } + } + } + } + + // Links in navbars + // + // Add a class to ensure links outside the navbar nav are colored correctly. + + .navbar-link { + color: $navbar-default-link-color; + &:hover { + color: $navbar-default-link-hover-color; + } + } + + .btn-link { + color: $navbar-default-link-color; + &:hover, + &:focus { + color: $navbar-default-link-hover-color; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: $navbar-default-link-disabled-color; + } + } + } +} + +// Inverse navbar + +.navbar-inverse { + background-color: $navbar-inverse-bg; + border-color: $navbar-inverse-border; + + .navbar-brand { + color: $navbar-inverse-brand-color; + &:hover, + &:focus { + color: $navbar-inverse-brand-hover-color; + background-color: $navbar-inverse-brand-hover-bg; + } + } + + .navbar-text { + color: $navbar-inverse-color; + } + + .navbar-nav { + > li > a { + color: $navbar-inverse-link-color; + + &:hover, + &:focus { + color: $navbar-inverse-link-hover-color; + background-color: $navbar-inverse-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: $navbar-inverse-link-active-color; + background-color: $navbar-inverse-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: $navbar-inverse-link-disabled-color; + background-color: $navbar-inverse-link-disabled-bg; + } + } + } + + // Darken the responsive nav toggle + .navbar-toggle { + border-color: $navbar-inverse-toggle-border-color; + &:hover, + &:focus { + background-color: $navbar-inverse-toggle-hover-bg; + } + .icon-bar { + background-color: $navbar-inverse-toggle-icon-bar-bg; + } + } + + .navbar-collapse, + .navbar-form { + border-color: darken($navbar-inverse-bg, 7%); + } + + // Dropdowns + .navbar-nav { + > .open > a { + &, + &:hover, + &:focus { + background-color: $navbar-inverse-link-active-bg; + color: $navbar-inverse-link-active-color; + } + } + + @media (max-width: $grid-float-breakpoint-max) { + // Dropdowns get custom display + .open .dropdown-menu { + > .dropdown-header { + border-color: $navbar-inverse-border; + } + .divider { + background-color: $navbar-inverse-border; + } + > li > a { + color: $navbar-inverse-link-color; + &:hover, + &:focus { + color: $navbar-inverse-link-hover-color; + background-color: $navbar-inverse-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: $navbar-inverse-link-active-color; + background-color: $navbar-inverse-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: $navbar-inverse-link-disabled-color; + background-color: $navbar-inverse-link-disabled-bg; + } + } + } + } + } + + .navbar-link { + color: $navbar-inverse-link-color; + &:hover { + color: $navbar-inverse-link-hover-color; + } + } + + .btn-link { + color: $navbar-inverse-link-color; + &:hover, + &:focus { + color: $navbar-inverse-link-hover-color; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: $navbar-inverse-link-disabled-color; + } + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_navs.scss b/openecomp-ui/resources/scss/bootstrap/_navs.scss new file mode 100644 index 0000000000..30a609851b --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_navs.scss @@ -0,0 +1,235 @@ +// +// Navs +// -------------------------------------------------- + +// Base class +// -------------------------------------------------- + +.nav { + margin-bottom: 0; + padding-left: 0; // Override default ul/ol + list-style: none; + @include clearfix; + + > li { + position: relative; + display: block; + + > a { + position: relative; + display: block; + padding: $nav-link-padding; + &:hover, + &:focus { + text-decoration: none; + background-color: $nav-link-hover-bg; + } + } + + // Disabled state sets text to gray and nukes hover/tab effects + &.disabled > a { + color: $nav-disabled-link-color; + + &:hover, + &:focus { + color: $nav-disabled-link-hover-color; + text-decoration: none; + background-color: transparent; + cursor: $cursor-disabled; + } + } + } + + // Open dropdowns + .open > a { + &, + &:hover, + &:focus { + background-color: $nav-link-hover-bg; + border-color: $link-color; + } + } + + // Nav dividers (deprecated with v3.0.1) + // + // This should have been removed in v3 with the dropping of `.nav-list`, but + // we missed it. We don't currently support this anywhere, but in the interest + // of maintaining backward compatibility in case you use it, it's deprecated. + .nav-divider { + @include nav-divider; + } + + // Prevent IE8 from misplacing imgs + // + // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 + > li > a > img { + max-width: none; + } +} + +// Tabs +// ------------------------- + +// Give the tabs something to sit on +.nav-tabs { + border-bottom: 1px solid $nav-tabs-border-color; + > li { + float: left; + // Make the list-items overlay the bottom border + margin-bottom: -1px; + + // Actual tabs (as links) + > a { + margin-right: 2px; + line-height: $line-height-base; + border: 1px solid transparent; + border-radius: $border-radius-base $border-radius-base 0 0; + &:hover { + border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color; + } + } + + // Active state, and its :hover to override normal :hover + &.active > a { + &, + &:hover, + &:focus { + color: $nav-tabs-active-link-hover-color; + background-color: $nav-tabs-active-link-hover-bg; + border: 1px solid $nav-tabs-active-link-hover-border-color; + border-bottom-color: transparent; + cursor: default; + } + } + } + // pulling this in mainly for less shorthand + &.nav-justified { + @extend .nav-justified; + @extend .nav-tabs-justified; + } +} + +// Pills +// ------------------------- +.nav-pills { + > li { + float: left; + + // Links rendered as pills + > a { + border-radius: $nav-pills-border-radius; + } + + li { + margin-left: 2px; + } + + // Active state + &.active > a { + &, + &:hover, + &:focus { + color: $nav-pills-active-link-hover-color; + background-color: $nav-pills-active-link-hover-bg; + } + } + } +} + +// Stacked pills +.nav-stacked { + > li { + float: none; + + li { + margin-top: 2px; + margin-left: 0; // no need for this gap between nav items + } + } +} + +// Nav variations +// -------------------------------------------------- + +// Justified nav links +// ------------------------- + +.nav-justified { + width: 100%; + + > li { + float: none; + > a { + text-align: center; + margin-bottom: 5px; + } + } + + > .dropdown .dropdown-menu { + top: auto; + left: auto; + } + + @media (min-width: $screen-sm-min) { + > li { + display: table-cell; + width: 1%; + > a { + margin-bottom: 0; + } + } + } +} + +// Move borders to anchors instead of bottom of list +// +// Mixin for adding on top the shared `.nav-justified` styles for our tabs +.nav-tabs-justified { + border-bottom: 0; + + > li > a { + // Override margin from .nav-tabs + margin-right: 0; + border-radius: $border-radius-base; + } + + > .active > a, + > .active > a:hover, + > .active > a:focus { + border: 1px solid $nav-tabs-justified-link-border-color; + } + + @media (min-width: $screen-sm-min) { + > li > a { + border-bottom: 1px solid $nav-tabs-justified-link-border-color; + border-radius: $border-radius-base $border-radius-base 0 0; + } + > .active > a, + > .active > a:hover, + > .active > a:focus { + border-bottom-color: $nav-tabs-justified-active-link-border-color; + } + } +} + +// Tabbable tabs +// ------------------------- + +// Hide tabbable panes to start, show them when `.active` +.tab-content { + > .tab-pane { + display: none; + } + > .active { + display: block; + } +} + +// Dropdowns +// ------------------------- + +// Specific dropdowns +.nav-tabs .dropdown-menu { + // make dropdown border overlap tab border + margin-top: -1px; + // Remove the top rounded corners here since there is a hard edge above the menu + @include border-top-radius(0); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_normalize.scss b/openecomp-ui/resources/scss/bootstrap/_normalize.scss new file mode 100644 index 0000000000..9dddf73ad2 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_normalize.scss @@ -0,0 +1,424 @@ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +// +// 1. Set default font family to sans-serif. +// 2. Prevent iOS and IE text size adjust after device orientation change, +// without disabling user zoom. +// + +html { + font-family: sans-serif; // 1 + -ms-text-size-adjust: 100%; // 2 + -webkit-text-size-adjust: 100%; // 2 +} + +// +// Remove default margin. +// + +body { + margin: 0; +} + +// HTML5 display definitions +// ========================================================================== + +// +// Correct `block` display not defined for any HTML5 element in IE 8/9. +// Correct `block` display not defined for `details` or `summary` in IE 10/11 +// and Firefox. +// Correct `block` display not defined for `main` in IE 11. +// + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +// +// 1. Correct `inline-block` display not defined in IE 8/9. +// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. +// + +audio, +canvas, +progress, +video { + display: inline-block; // 1 + vertical-align: baseline; // 2 +} + +// +// Prevent modern browsers from displaying `audio` without controls. +// Remove excess height in iOS 5 devices. +// + +audio:not([controls]) { + display: none; + height: 0; +} + +// +// Address `[hidden]` styling not present in IE 8/9/10. +// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. +// + +[hidden], +template { + display: none; +} + +// Links +// ========================================================================== + +// +// Remove the gray background color from active links in IE 10. +// + +a { + background-color: transparent; +} + +// +// Improve readability of focused elements when they are also in an +// active/hover state. +// + +a:active, +a:hover { + outline: 0; +} + +// Text-level semantics +// ========================================================================== + +// +// Address styling not present in IE 8/9/10/11, Safari, and Chrome. +// + +abbr[title] { + border-bottom: 1px dotted; +} + +// +// Address style set to `bolder` in Firefox 4+, Safari, and Chrome. +// + +b, +strong { + font-weight: bold; +} + +// +// Address styling not present in Safari and Chrome. +// + +dfn { + font-style: italic; +} + +// +// Address variable `h1` font-size and margin within `section` and `article` +// contexts in Firefox 4+, Safari, and Chrome. +// + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +// +// Address styling not present in IE 8/9. +// + +mark { + background: #ff0; + color: #000; +} + +// +// Address inconsistent and variable font size in all browsers. +// + +small { + font-size: 80%; +} + +// +// Prevent `sub` and `sup` affecting `line-height` in all browsers. +// + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +// Embedded content +// ========================================================================== + +// +// Remove border when inside `a` element in IE 8/9/10. +// + +img { + border: 0; +} + +// +// Correct overflow not hidden in IE 9/10/11. +// + +svg:not(:root) { + overflow: hidden; +} + +// Grouping content +// ========================================================================== + +// +// Address margin not present in IE 8/9 and Safari. +// + +figure { + margin: 1em 40px; +} + +// +// Address differences between Firefox and other browsers. +// + +hr { + box-sizing: content-box; + height: 0; +} + +// +// Contain overflow in all browsers. +// + +pre { + overflow: auto; +} + +// +// Address odd `em`-unit font size rendering in all browsers. +// + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +// Forms +// ========================================================================== + +// +// Known limitation: by default, Chrome and Safari on OS X allow very limited +// styling of `select`, unless a `border` property is set. +// + +// +// 1. Correct color not being inherited. +// Known issue: affects color of disabled elements. +// 2. Correct font properties not being inherited. +// 3. Address margins set differently in Firefox 4+, Safari, and Chrome. +// + +button, +input, +optgroup, +select, +textarea { + color: inherit; // 1 + font: inherit; // 2 + margin: 0; // 3 +} + +// +// Address `overflow` set to `hidden` in IE 8/9/10/11. +// + +button { + overflow: visible; +} + +// +// Address inconsistent `text-transform` inheritance for `button` and `select`. +// All other form control elements do not inherit `text-transform` values. +// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. +// Correct `select` style inheritance in Firefox. +// + +button, +select { + text-transform: none; +} + +// +// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` +// and `video` controls. +// 2. Correct inability to style clickable `input` types in iOS. +// 3. Improve usability and consistency of cursor style between image-type +// `input` and others. +// + +button, +html input[type="button"], // 1 +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; // 2 + cursor: pointer; // 3 +} + +// +// Re-set default cursor for disabled elements. +// + +button[disabled], +html input[disabled] { + cursor: default; +} + +// +// Remove inner padding and border in Firefox 4+. +// + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +// +// Address Firefox 4+ setting `line-height` on `input` using `!important` in +// the UA stylesheet. +// + +input { + line-height: normal; +} + +// +// It's recommended that you don't attempt to style these elements. +// Firefox's implementation doesn't respect box-sizing, padding, or width. +// +// 1. Address box sizing set to `content-box` in IE 8/9/10. +// 2. Remove excess padding in IE 8/9/10. +// + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; // 1 + padding: 0; // 2 +} + +// +// Fix the cursor style for Chrome's increment/decrement buttons. For certain +// `font-size` values of the `input`, it causes the cursor style of the +// decrement button to change from `default` to `text`. +// + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +// +// 1. Address `appearance` set to `searchfield` in Safari and Chrome. +// 2. Address `box-sizing` set to `border-box` in Safari and Chrome. +// + +input[type="search"] { + -webkit-appearance: textfield; // 1 + box-sizing: content-box; //2 +} + +// +// Remove inner padding and search cancel button in Safari and Chrome on OS X. +// Safari (but not Chrome) clips the cancel button when the search input has +// padding (and `textfield` appearance). +// + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +// +// Define consistent border, margin, and padding. +// + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +// +// 1. Correct `color` not being inherited in IE 8/9/10/11. +// 2. Remove padding so people aren't caught out if they zero out fieldsets. +// + +legend { + border: 0; // 1 + padding: 0; // 2 +} + +// +// Remove default vertical scrollbar in IE 8/9/10/11. +// + +textarea { + overflow: auto; +} + +// +// Don't inherit the `font-weight` (applied by a rule above). +// NOTE: the default cannot safely be changed in Chrome and Safari on OS X. +// + +optgroup { + font-weight: bold; +} + +// Tables +// ========================================================================== + +// +// Remove most spacing between table cells. +// + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_pager.scss b/openecomp-ui/resources/scss/bootstrap/_pager.scss new file mode 100644 index 0000000000..5799c9bd54 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_pager.scss @@ -0,0 +1,53 @@ +// +// Pager pagination +// -------------------------------------------------- + +.pager { + padding-left: 0; + margin: $line-height-computed 0; + list-style: none; + text-align: center; + @include clearfix; + li { + display: inline; + > a, + > span { + display: inline-block; + padding: 5px 14px; + background-color: $pager-bg; + border: 1px solid $pager-border; + border-radius: $pager-border-radius; + } + + > a:hover, + > a:focus { + text-decoration: none; + background-color: $pager-hover-bg; + } + } + + .next { + > a, + > span { + float: right; + } + } + + .previous { + > a, + > span { + float: left; + } + } + + .disabled { + > a, + > a:hover, + > a:focus, + > span { + color: $pager-disabled-color; + background-color: $pager-bg; + cursor: $cursor-disabled; + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_pagination.scss b/openecomp-ui/resources/scss/bootstrap/_pagination.scss new file mode 100644 index 0000000000..882cee1f0d --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_pagination.scss @@ -0,0 +1,89 @@ +// +// Pagination (multiple pages) +// -------------------------------------------------- +.pagination { + display: inline-block; + padding-left: 0; + margin: $line-height-computed 0; + border-radius: $border-radius-base; + + > li { + display: inline; // Remove list-style and block-level defaults + > a, + > span { + position: relative; + float: left; // Collapse white-space + padding: $padding-base-vertical $padding-base-horizontal; + line-height: $line-height-base; + text-decoration: none; + color: $pagination-color; + background-color: $pagination-bg; + border: 1px solid $pagination-border; + margin-left: -1px; + } + &:first-child { + > a, + > span { + margin-left: 0; + @include border-left-radius($border-radius-base); + } + } + &:last-child { + > a, + > span { + @include border-right-radius($border-radius-base); + } + } + } + + > li > a, + > li > span { + &:hover, + &:focus { + z-index: 3; + color: $pagination-hover-color; + background-color: $pagination-hover-bg; + border-color: $pagination-hover-border; + } + } + + > .active > a, + > .active > span { + &, + &:hover, + &:focus { + z-index: 2; + color: $pagination-active-color; + background-color: $pagination-active-bg; + border-color: $pagination-active-border; + cursor: default; + } + } + + > .disabled { + > span, + > span:hover, + > span:focus, + > a, + > a:hover, + > a:focus { + color: $pagination-disabled-color; + background-color: $pagination-disabled-bg; + border-color: $pagination-disabled-border; + cursor: $cursor-disabled; + } + } +} + +// Sizing +// -------------------------------------------------- + +// Large +.pagination-lg { + @include pagination-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $border-radius-large); +} + +// Small +.pagination-sm { + @include pagination-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $border-radius-small); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_panels.scss b/openecomp-ui/resources/scss/bootstrap/_panels.scss new file mode 100644 index 0000000000..f37b58147b --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_panels.scss @@ -0,0 +1,274 @@ +// +// Panels +// -------------------------------------------------- + +// Base class +.panel { + margin-bottom: $line-height-computed; + background-color: $panel-bg; + border: 1px solid transparent; + border-radius: $panel-border-radius; + @include box-shadow(0 1px 1px rgba(0, 0, 0, .05)); +} + +// Panel contents +.panel-body { + padding: $panel-body-padding; + @include clearfix; +} + +// Optional heading +.panel-heading { + padding: $panel-heading-padding; + border-bottom: 1px solid transparent; + @include border-top-radius(($panel-border-radius - 1)); + + > .dropdown .dropdown-toggle { + color: inherit; + } +} + +// Within heading, strip any `h*` tag of its default margins for spacing. +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: ceil(($font-size-base * 1.125)); + color: inherit; + + > a, + > small, + > .small, + > small > a, + > .small > a { + color: inherit; + } +} + +// Optional footer (stays gray in every modifier class) +.panel-footer { + padding: $panel-footer-padding; + background-color: $panel-footer-bg; + border-top: 1px solid $panel-inner-border; + @include border-bottom-radius(($panel-border-radius - 1)); +} + +// List groups in panels +// +// By default, space out list group content from panel headings to account for +// any kind of custom content between the two. + +.panel { + > .list-group, + > .panel-collapse > .list-group { + margin-bottom: 0; + + .list-group-item { + border-width: 1px 0; + border-radius: 0; + } + + // Add border top radius for first one + &:first-child { + .list-group-item:first-child { + border-top: 0; + @include border-top-radius(($panel-border-radius - 1)); + } + } + + // Add border bottom radius for last one + &:last-child { + .list-group-item:last-child { + border-bottom: 0; + @include border-bottom-radius(($panel-border-radius - 1)); + } + } + } + > .panel-heading + .panel-collapse > .list-group { + .list-group-item:first-child { + @include border-top-radius(0); + } + } +} + +// Collapse space between when there's no additional content. +.panel-heading + .list-group { + .list-group-item:first-child { + border-top-width: 0; + } +} + +.list-group + .panel-footer { + border-top-width: 0; +} + +// Tables in panels +// +// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and +// watch it go full width. + +.panel { + > .table, + > .table-responsive > .table, + > .panel-collapse > .table { + margin-bottom: 0; + + caption { + padding-left: $panel-body-padding; + padding-right: $panel-body-padding; + } + } + // Add border top radius for first one + > .table:first-child, + > .table-responsive:first-child > .table:first-child { + @include border-top-radius(($panel-border-radius - 1)); + + > thead:first-child, + > tbody:first-child { + > tr:first-child { + border-top-left-radius: ($panel-border-radius - 1); + border-top-right-radius: ($panel-border-radius - 1); + + td:first-child, + th:first-child { + border-top-left-radius: ($panel-border-radius - 1); + } + td:last-child, + th:last-child { + border-top-right-radius: ($panel-border-radius - 1); + } + } + } + } + // Add border bottom radius for last one + > .table:last-child, + > .table-responsive:last-child > .table:last-child { + @include border-bottom-radius(($panel-border-radius - 1)); + + > tbody:last-child, + > tfoot:last-child { + > tr:last-child { + border-bottom-left-radius: ($panel-border-radius - 1); + border-bottom-right-radius: ($panel-border-radius - 1); + + td:first-child, + th:first-child { + border-bottom-left-radius: ($panel-border-radius - 1); + } + td:last-child, + th:last-child { + border-bottom-right-radius: ($panel-border-radius - 1); + } + } + } + } + > .panel-body + .table, + > .panel-body + .table-responsive, + > .table + .panel-body, + > .table-responsive + .panel-body { + border-top: 1px solid $table-border-color; + } + > .table > tbody:first-child > tr:first-child th, + > .table > tbody:first-child > tr:first-child td { + border-top: 0; + } + > .table-bordered, + > .table-responsive > .table-bordered { + border: 0; + > thead, + > tbody, + > tfoot { + > tr { + > th:first-child, + > td:first-child { + border-left: 0; + } + > th:last-child, + > td:last-child { + border-right: 0; + } + } + } + > thead, + > tbody { + > tr:first-child { + > td, + > th { + border-bottom: 0; + } + } + } + > tbody, + > tfoot { + > tr:last-child { + > td, + > th { + border-bottom: 0; + } + } + } + } + > .table-responsive { + border: 0; + margin-bottom: 0; + } +} + +// Collapsable panels (aka, accordion) +// +// Wrap a series of panels in `.panel-group` to turn them into an accordion with +// the help of our collapse JavaScript plugin. + +.panel-group { + margin-bottom: $line-height-computed; + + // Tighten up margin so it's only between panels + .panel { + margin-bottom: 0; + border-radius: $panel-border-radius; + + + .panel { + margin-top: 5px; + } + } + + .panel-heading { + border-bottom: 0; + + + .panel-collapse > .panel-body, + + .panel-collapse > .list-group { + border-top: 1px solid $panel-inner-border; + } + } + + .panel-footer { + border-top: 0; + + .panel-collapse .panel-body { + border-bottom: 1px solid $panel-inner-border; + } + } +} + +// Contextual variations +.panel-default { + @include panel-variant($panel-default-border, $panel-default-text, $panel-default-heading-bg, $panel-default-border); +} + +.panel-primary { + @include panel-variant($panel-primary-border, $panel-primary-text, $panel-primary-heading-bg, $panel-primary-border); +} + +.panel-success { + @include panel-variant($panel-success-border, $panel-success-text, $panel-success-heading-bg, $panel-success-border); +} + +.panel-info { + @include panel-variant($panel-info-border, $panel-info-text, $panel-info-heading-bg, $panel-info-border); +} + +.panel-warning { + @include panel-variant($panel-warning-border, $panel-warning-text, $panel-warning-heading-bg, $panel-warning-border); +} + +.panel-danger { + @include panel-variant($panel-danger-border, $panel-danger-text, $panel-danger-heading-bg, $panel-danger-border); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_popovers.scss b/openecomp-ui/resources/scss/bootstrap/_popovers.scss new file mode 100644 index 0000000000..73d0bf27bf --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_popovers.scss @@ -0,0 +1,140 @@ +// +// Popovers +// -------------------------------------------------- + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: $zindex-popover; + display: none; + max-width: $popover-max-width; + padding: 1px; + // Our parent element can be arbitrary since popovers are by default inserted as a sibling of their target element. + // So reset our font and text properties to avoid inheriting weird values. + @include reset-text; + font-size: $font-size-base; + + background-color: $popover-bg; + background-clip: padding-box; + border: 1px solid $popover-fallback-border-color; + border: 1px solid $popover-border-color; + border-radius: $border-radius-large; + @include box-shadow(0 5px 10px rgba(0, 0, 0, .2)); + + // Offset the popover to account for the popover arrow + &.top { + margin-top: -$popover-arrow-width; + } + &.right { + margin-left: $popover-arrow-width; + } + &.bottom { + margin-top: $popover-arrow-width; + } + &.left { + margin-left: -$popover-arrow-width; + } +} + +.popover-title { + margin: 0; // reset heading margin + padding: 8px 14px; + font-size: $font-size-base; + background-color: $popover-title-bg; + border-bottom: 1px solid darken($popover-title-bg, 5%); + border-radius: ($border-radius-large - 1) ($border-radius-large - 1) 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +// Arrows +// +// .arrow is outer, .arrow:after is inner + +.popover > .arrow { + &, + &:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + } +} + +.popover > .arrow { + border-width: $popover-arrow-outer-width; +} + +.popover > .arrow:after { + border-width: $popover-arrow-width; + content: ""; +} + +.popover { + &.top > .arrow { + left: 50%; + margin-left: -$popover-arrow-outer-width; + border-bottom-width: 0; + border-top-color: $popover-arrow-outer-fallback-color; // IE8 fallback + border-top-color: $popover-arrow-outer-color; + bottom: -$popover-arrow-outer-width; + &:after { + content: " "; + bottom: 1px; + margin-left: -$popover-arrow-width; + border-bottom-width: 0; + border-top-color: $popover-arrow-color; + } + } + &.right > .arrow { + top: 50%; + left: -$popover-arrow-outer-width; + margin-top: -$popover-arrow-outer-width; + border-left-width: 0; + border-right-color: $popover-arrow-outer-fallback-color; // IE8 fallback + border-right-color: $popover-arrow-outer-color; + &:after { + content: " "; + left: 1px; + bottom: -$popover-arrow-width; + border-left-width: 0; + border-right-color: $popover-arrow-color; + } + } + &.bottom > .arrow { + left: 50%; + margin-left: -$popover-arrow-outer-width; + border-top-width: 0; + border-bottom-color: $popover-arrow-outer-fallback-color; // IE8 fallback + border-bottom-color: $popover-arrow-outer-color; + top: -$popover-arrow-outer-width; + &:after { + content: " "; + top: 1px; + margin-left: -$popover-arrow-width; + border-top-width: 0; + border-bottom-color: $popover-arrow-color; + } + } + + &.left > .arrow { + top: 50%; + right: -$popover-arrow-outer-width; + margin-top: -$popover-arrow-outer-width; + border-right-width: 0; + border-left-color: $popover-arrow-outer-fallback-color; // IE8 fallback + border-left-color: $popover-arrow-outer-color; + &:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: $popover-arrow-color; + bottom: -$popover-arrow-width; + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_print.scss b/openecomp-ui/resources/scss/bootstrap/_print.scss new file mode 100644 index 0000000000..fa05d1a007 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_print.scss @@ -0,0 +1,101 @@ +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ + +// ========================================================================== +// Print styles. +// Inlined to avoid the additional HTTP request: h5bp.com/r +// ========================================================================== + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; // Black prints faster: h5bp.com/s + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links that are fragment identifiers, + // or use the `javascript:` pseudo protocol + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + + // Bootstrap specific changes start + + // Bootstrap components + .navbar { + display: none; + } + .btn, + .dropup > .btn { + > .caret { + border-top-color: #000 !important; + } + } + .label { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + + td, + th { + background-color: #fff !important; + } + } + .table-bordered { + th, + td { + border: 1px solid #ddd !important; + } + } + + // Bootstrap specific changes end +} diff --git a/openecomp-ui/resources/scss/bootstrap/_progress-bars.scss b/openecomp-ui/resources/scss/bootstrap/_progress-bars.scss new file mode 100644 index 0000000000..d99fc49dec --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_progress-bars.scss @@ -0,0 +1,92 @@ +// +// Progress bars +// -------------------------------------------------- + +// Bar animations +// ------------------------- + +// WebKit +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +// Spec and IE10+ +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +// Bar itself +// ------------------------- + +// Outer container +.progress { + overflow: hidden; + height: $line-height-computed; + margin-bottom: $line-height-computed; + background-color: $progress-bg; + border-radius: $progress-border-radius; + @include box-shadow(inset 0 1px 2px rgba(0, 0, 0, .1)); +} + +// Bar of progress +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: $font-size-small; + line-height: $line-height-computed; + color: $progress-bar-color; + text-align: center; + background-color: $progress-bar-bg; + @include box-shadow(inset 0 -1px 0 rgba(0, 0, 0, .15)); + @include transition(width .6s ease); +} + +// Striped bars +// +// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the +// `.progress-bar-striped` class, which you just add to an existing +// `.progress-bar`. +.progress-striped .progress-bar, +.progress-bar-striped { + @include gradient-striped; + background-size: 40px 40px; +} + +// Call animation for the active one +// +// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the +// `.progress-bar.active` approach. +.progress.active .progress-bar, +.progress-bar.active { + @include animation(progress-bar-stripes 2s linear infinite); +} + +// Variations +// ------------------------- + +.progress-bar-success { + @include progress-bar-variant($progress-bar-success-bg); +} + +.progress-bar-info { + @include progress-bar-variant($progress-bar-info-bg); +} + +.progress-bar-warning { + @include progress-bar-variant($progress-bar-warning-bg); +} + +.progress-bar-danger { + @include progress-bar-variant($progress-bar-danger-bg); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_responsive-embed.scss b/openecomp-ui/resources/scss/bootstrap/_responsive-embed.scss new file mode 100644 index 0000000000..282589f97c --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_responsive-embed.scss @@ -0,0 +1,35 @@ +// Embeds responsive +// +// Credit: Nicolas Gallagher and SUIT CSS. + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; + + .embed-responsive-item, + iframe, + embed, + object, + video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0; + } +} + +// Modifier class for 16:9 aspect ratio +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} + +// Modifier class for 4:3 aspect ratio +.embed-responsive-4by3 { + padding-bottom: 75%; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_responsive-utilities.scss b/openecomp-ui/resources/scss/bootstrap/_responsive-utilities.scss new file mode 100644 index 0000000000..915d723599 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_responsive-utilities.scss @@ -0,0 +1,191 @@ +// +// Responsive: Utility classes +// -------------------------------------------------- + +// IE10 in Windows (Phone) 8 +// +// Support for responsive views via media queries is kind of borked in IE10, for +// Surface/desktop in split view and for Windows Phone 8. This particular fix +// must be accompanied by a snippet of JavaScript to sniff the user agent and +// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at +// our Getting Started page for more information on this bug. +// +// For more information, see the following: +// +// Issue: https://github.com/twbs/bootstrap/issues/10497 +// Docs: http://getbootstrap.com/getting-started/#support-ie10-width +// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/ +// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ + +@at-root { + @-ms-viewport { + width: device-width; + } +} + +// Visibility utilities +// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0 + +@include responsive-invisibility('.visible-xs'); +@include responsive-invisibility('.visible-sm'); +@include responsive-invisibility('.visible-md'); +@include responsive-invisibility('.visible-lg'); + +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} + +@media (max-width: $screen-xs-max) { + @include responsive-visibility('.visible-xs'); +} + +.visible-xs-block { + @media (max-width: $screen-xs-max) { + display: block !important; + } +} + +.visible-xs-inline { + @media (max-width: $screen-xs-max) { + display: inline !important; + } +} + +.visible-xs-inline-block { + @media (max-width: $screen-xs-max) { + display: inline-block !important; + } +} + +@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + @include responsive-visibility('.visible-sm'); +} + +.visible-sm-block { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + display: block !important; + } +} + +.visible-sm-inline { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + display: inline !important; + } +} + +.visible-sm-inline-block { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + display: inline-block !important; + } +} + +@media (min-width: $screen-md-min) and (max-width: $screen-md-max) { + @include responsive-visibility('.visible-md'); +} + +.visible-md-block { + @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { + display: block !important; + } +} + +.visible-md-inline { + @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { + display: inline !important; + } +} + +.visible-md-inline-block { + @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { + display: inline-block !important; + } +} + +@media (min-width: $screen-lg-min) { + @include responsive-visibility('.visible-lg'); +} + +.visible-lg-block { + @media (min-width: $screen-lg-min) { + display: block !important; + } +} + +.visible-lg-inline { + @media (min-width: $screen-lg-min) { + display: inline !important; + } +} + +.visible-lg-inline-block { + @media (min-width: $screen-lg-min) { + display: inline-block !important; + } +} + +@media (max-width: $screen-xs-max) { + @include responsive-invisibility('.hidden-xs'); +} + +@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + @include responsive-invisibility('.hidden-sm'); +} + +@media (min-width: $screen-md-min) and (max-width: $screen-md-max) { + @include responsive-invisibility('.hidden-md'); +} + +@media (min-width: $screen-lg-min) { + @include responsive-invisibility('.hidden-lg'); +} + +// Print utilities +// +// Media queries are placed on the inside to be mixin-friendly. + +// Note: Deprecated .visible-print as of v3.2.0 + +@include responsive-invisibility('.visible-print'); + +@media print { + @include responsive-visibility('.visible-print'); +} + +.visible-print-block { + display: none !important; + + @media print { + display: block !important; + } +} + +.visible-print-inline { + display: none !important; + + @media print { + display: inline !important; + } +} + +.visible-print-inline-block { + display: none !important; + + @media print { + display: inline-block !important; + } +} + +@media print { + @include responsive-invisibility('.hidden-print'); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_scaffolding.scss b/openecomp-ui/resources/scss/bootstrap/_scaffolding.scss new file mode 100644 index 0000000000..910afd55e5 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_scaffolding.scss @@ -0,0 +1,154 @@ +// +// Scaffolding +// -------------------------------------------------- + +// Reset the box-sizing +// +// Heads up! This reset may cause conflicts with some third-party widgets. +// For recommendations on resolving such conflicts, see +// http://getbootstrap.com/getting-started/#third-box-sizing +* { + @include box-sizing(border-box); +} + +*:before, +*:after { + @include box-sizing(border-box); +} + +// Body reset + +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +body { + font-family: $font-family-base; + font-size: $font-size-base; + line-height: $line-height-base; + color: $text-color; + background-color: $body-bg; +} + +// Reset fonts for relevant elements +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +// Links + +a { + color: $link-color; + text-decoration: none; + + &:hover, + &:focus { + color: $link-hover-color; + text-decoration: $link-hover-decoration; + } + + &:focus { + @include tab-focus; + } +} + +// Figures +// +// We reset this here because previously Normalize had no `figure` margins. This +// ensures we don't break anyone's use of the element. + +figure { + margin: 0; +} + +// Images + +img { + vertical-align: middle; +} + +// Responsive images (ensure images don't scale beyond their parents) +.img-responsive { + @include img-responsive; +} + +// Rounded corners +.img-rounded { + border-radius: $border-radius-large; +} + +// Image thumbnails +// +// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`. +.img-thumbnail { + padding: $thumbnail-padding; + line-height: $line-height-base; + background-color: $thumbnail-bg; + border: 1px solid $thumbnail-border; + border-radius: $thumbnail-border-radius; + @include transition(all .2s ease-in-out); + + // Keep them at most 100% wide + @include img-responsive(inline-block); +} + +// Perfect circle +.img-circle { + border-radius: 50%; // set radius in percents +} + +// Horizontal rules + +hr { + margin-top: $line-height-computed; + margin-bottom: $line-height-computed; + border: 0; + border-top: 1px solid $hr-border; +} + +// Only display content to screen readers +// +// See: http://a11yproject.com/posts/how-to-hide-content/ + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + +// Use in conjunction with .sr-only to only display content when it's focused. +// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 +// Credit: HTML5 Boilerplate + +.sr-only-focusable { + &:active, + &:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; + } +} + +// iOS "clickable elements" fix for role="button" +// +// Fixes "clickability" issue (and more generally, the firing of events such as focus as well) +// for traditionally non-focusable elements with role="button" +// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile + +[role="button"] { + cursor: pointer; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_tables.scss b/openecomp-ui/resources/scss/bootstrap/_tables.scss new file mode 100644 index 0000000000..39379abba2 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_tables.scss @@ -0,0 +1,228 @@ +// +// Tables +// -------------------------------------------------- + +table { + background-color: $table-bg; +} + +caption { + padding-top: $table-cell-padding; + padding-bottom: $table-cell-padding; + color: $text-muted; + text-align: left; +} + +th { + text-align: left; +} + +// Baseline styles + +.table { + width: 100%; + max-width: 100%; + margin-bottom: $line-height-computed; + // Cells + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + padding: $table-cell-padding; + line-height: $line-height-base; + vertical-align: top; + border-top: 1px solid $table-border-color; + } + } + } + // Bottom align for column headings + > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid $table-border-color; + } + // Remove top border from thead by default + > caption + thead, + > colgroup + thead, + > thead:first-child { + > tr:first-child { + > th, + > td { + border-top: 0; + } + } + } + // Account for multiple tbody instances + > tbody + tbody { + border-top: 2px solid $table-border-color; + } + + // Nesting + .table { + background-color: $body-bg; + } +} + +// Condensed table w/ half padding + +.table-condensed { + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + padding: $table-condensed-cell-padding; + } + } + } +} + +// Bordered version +// +// Add borders all around the table and between all the columns. + +.table-bordered { + border: 1px solid $table-border-color; + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + border: 1px solid $table-border-color; + } + } + } + > thead > tr { + > th, + > td { + border-bottom-width: 2px; + } + } +} + +// Zebra-striping +// +// Default zebra-stripe styles (alternating gray and transparent backgrounds) + +.table-striped { + > tbody > tr:nth-of-type(odd) { + background-color: $table-bg-accent; + } +} + +// Hover effect +// +// Placed here since it has to come after the potential zebra striping + +.table-hover { + > tbody > tr:hover { + background-color: $table-bg-hover; + } +} + +// Table cell sizing +// +// Reset default table behavior + +table col[class*="col-"] { + position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623) + float: none; + display: table-column; +} + +table { + td, + th { + &[class*="col-"] { + position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623) + float: none; + display: table-cell; + } + } +} + +// Table backgrounds +// +// Exact selectors below required to override `.table-striped` and prevent +// inheritance to nested tables. + +// Generate the contextual variants +@include table-row-variant('active', $table-bg-active); +@include table-row-variant('success', $state-success-bg); +@include table-row-variant('info', $state-info-bg); +@include table-row-variant('warning', $state-warning-bg); +@include table-row-variant('danger', $state-danger-bg); + +// Responsive tables +// +// Wrap your tables in `.table-responsive` and we'll make them mobile friendly +// by enabling horizontal scrolling. Only applies <768px. Everything above that +// will display normally. + +.table-responsive { + overflow-x: auto; + min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837) + + @media screen and (max-width: $screen-xs-max) { + width: 100%; + margin-bottom: ($line-height-computed * 0.75); + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid $table-border-color; + + // Tighten up spacing + > .table { + margin-bottom: 0; + + // Ensure the content doesn't wrap + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + white-space: nowrap; + } + } + } + } + + // Special overrides for the bordered tables + > .table-bordered { + border: 0; + + // Nuke the appropriate borders so that the parent can handle them + > thead, + > tbody, + > tfoot { + > tr { + > th:first-child, + > td:first-child { + border-left: 0; + } + > th:last-child, + > td:last-child { + border-right: 0; + } + } + } + + // Only nuke the last row's bottom-border in `tbody` and `tfoot` since + // chances are there will be only one `tr` in a `thead` and that would + // remove the border altogether. + > tbody, + > tfoot { + > tr:last-child { + > th, + > td { + border-bottom: 0; + } + } + } + + } + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_theme.scss b/openecomp-ui/resources/scss/bootstrap/_theme.scss new file mode 100644 index 0000000000..dfec418770 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_theme.scss @@ -0,0 +1,344 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +// +// Load core variables and mixins +// -------------------------------------------------- +@import "variables"; +@import "mixins"; + +// +// Buttons +// -------------------------------------------------- + +// Common styles +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + $shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + @include box-shadow($shadow); + + // Reset the shadow + &:active, + &.active { + @include box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125)); + } + + &.disabled, + &[disabled], + fieldset[disabled] & { + @include box-shadow(none); + } + + .badge { + text-shadow: none; + } +} + +// Mixin for generating new styles +@mixin btn-styles($btn-color: #555) { + @include gradient-vertical($start-color: $btn-color, $end-color: darken($btn-color, 12%)); + @include reset-filter; // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620 + background-repeat: repeat-x; + border-color: darken($btn-color, 14%); + + &:hover, + &:focus { + background-color: darken($btn-color, 12%); + background-position: 0 -15px; + } + + &:active, + &.active { + background-color: darken($btn-color, 12%); + border-color: darken($btn-color, 14%); + } + + &.disabled, + &[disabled], + fieldset[disabled] & { + &, + &:hover, + &:focus, + &.focus, + &:active, + &.active { + background-color: darken($btn-color, 12%); + background-image: none; + } + } +} + +// Common styles +.btn { + // Remove the gradient for the pressed/active state + &:active, + &.active { + background-image: none; + } +} + +// Apply the mixin to the buttons +.btn-default { + @include btn-styles($btn-default-bg); + text-shadow: 0 1px 0 #fff; + border-color: #ccc; +} + +.btn-primary { + @include btn-styles($btn-primary-bg); +} + +.btn-success { + @include btn-styles($btn-success-bg); +} + +.btn-info { + @include btn-styles($btn-info-bg); +} + +.btn-warning { + @include btn-styles($btn-warning-bg); +} + +.btn-danger { + @include btn-styles($btn-danger-bg); +} + +// +// Images +// -------------------------------------------------- + +.thumbnail, +.img-thumbnail { + @include box-shadow(0 1px 2px rgba(0, 0, 0, .075)); +} + +// +// Dropdowns +// -------------------------------------------------- + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + @include gradient-vertical($start-color: $dropdown-link-hover-bg, $end-color: darken($dropdown-link-hover-bg, 5%)); + background-color: darken($dropdown-link-hover-bg, 5%); +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + @include gradient-vertical($start-color: $dropdown-link-active-bg, $end-color: darken($dropdown-link-active-bg, 5%)); + background-color: darken($dropdown-link-active-bg, 5%); +} + +// +// Navbar +// -------------------------------------------------- + +// Default navbar +.navbar-default { + @include gradient-vertical($start-color: lighten($navbar-default-bg, 10%), $end-color: $navbar-default-bg); + @include reset-filter; // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered + border-radius: $navbar-border-radius; + $shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + @include box-shadow($shadow); + + .navbar-nav > .open > a, + .navbar-nav > .active > a { + @include gradient-vertical($start-color: darken($navbar-default-link-active-bg, 5%), $end-color: darken($navbar-default-link-active-bg, 2%)); + @include box-shadow(inset 0 3px 9px rgba(0, 0, 0, .075)); + } +} + +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} + +// Inverted navbar +.navbar-inverse { + @include gradient-vertical($start-color: lighten($navbar-inverse-bg, 10%), $end-color: $navbar-inverse-bg); + @include reset-filter; // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257 + border-radius: $navbar-border-radius; + .navbar-nav > .open > a, + .navbar-nav > .active > a { + @include gradient-vertical($start-color: $navbar-inverse-link-active-bg, $end-color: lighten($navbar-inverse-link-active-bg, 2.5%)); + @include box-shadow(inset 0 3px 9px rgba(0, 0, 0, .25)); + } + + .navbar-brand, + .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); + } +} + +// Undo rounded corners in static and fixed navbars +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} + +// Fix active state of dropdown items in collapsed mode +@media (max-width: $grid-float-breakpoint-max) { + .navbar .navbar-nav .open .dropdown-menu > .active > a { + &, + &:hover, + &:focus { + color: #fff; + @include gradient-vertical($start-color: $dropdown-link-active-bg, $end-color: darken($dropdown-link-active-bg, 5%)); + } + } +} + +// +// Alerts +// -------------------------------------------------- + +// Common styles +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + $shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + @include box-shadow($shadow); +} + +// Mixin for generating new styles +@mixin alert-styles($color) { + @include gradient-vertical($start-color: $color, $end-color: darken($color, 7.5%)); + border-color: darken($color, 15%); +} + +// Apply the mixin to the alerts +.alert-success { + @include alert-styles($alert-success-bg); +} + +.alert-info { + @include alert-styles($alert-info-bg); +} + +.alert-warning { + @include alert-styles($alert-warning-bg); +} + +.alert-danger { + @include alert-styles($alert-danger-bg); +} + +// +// Progress bars +// -------------------------------------------------- + +// Give the progress background some depth +.progress { + @include gradient-vertical($start-color: darken($progress-bg, 4%), $end-color: $progress-bg) +} + +// Mixin for generating new styles +@mixin progress-bar-styles($color) { + @include gradient-vertical($start-color: $color, $end-color: darken($color, 10%)); +} + +// Apply the mixin to the progress bars +.progress-bar { + @include progress-bar-styles($progress-bar-bg); +} + +.progress-bar-success { + @include progress-bar-styles($progress-bar-success-bg); +} + +.progress-bar-info { + @include progress-bar-styles($progress-bar-info-bg); +} + +.progress-bar-warning { + @include progress-bar-styles($progress-bar-warning-bg); +} + +.progress-bar-danger { + @include progress-bar-styles($progress-bar-danger-bg); +} + +// Reset the striped class because our mixins don't do multiple gradients and +// the above custom styles override the new `.progress-bar-striped` in v3.2.0. +.progress-bar-striped { + @include gradient-striped; +} + +// +// List groups +// -------------------------------------------------- + +.list-group { + border-radius: $border-radius-base; + @include box-shadow(0 1px 2px rgba(0, 0, 0, .075)); +} + +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 darken($list-group-active-bg, 10%); + @include gradient-vertical($start-color: $list-group-active-bg, $end-color: darken($list-group-active-bg, 7.5%)); + border-color: darken($list-group-active-border, 7.5%); + + .badge { + text-shadow: none; + } +} + +// +// Panels +// -------------------------------------------------- + +// Common styles +.panel { + @include box-shadow(0 1px 2px rgba(0, 0, 0, .05)); +} + +// Mixin for generating new styles +@mixin panel-heading-styles($color) { + @include gradient-vertical($start-color: $color, $end-color: darken($color, 5%)); +} + +// Apply the mixin to the panel headings only +.panel-default > .panel-heading { + @include panel-heading-styles($panel-default-heading-bg); +} + +.panel-primary > .panel-heading { + @include panel-heading-styles($panel-primary-heading-bg); +} + +.panel-success > .panel-heading { + @include panel-heading-styles($panel-success-heading-bg); +} + +.panel-info > .panel-heading { + @include panel-heading-styles($panel-info-heading-bg); +} + +.panel-warning > .panel-heading { + @include panel-heading-styles($panel-warning-heading-bg); +} + +.panel-danger > .panel-heading { + @include panel-heading-styles($panel-danger-heading-bg); +} + +// +// Wells +// -------------------------------------------------- + +.well { + @include gradient-vertical($start-color: darken($well-bg, 5%), $end-color: $well-bg); + border-color: darken($well-bg, 10%); + $shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + @include box-shadow($shadow); +} diff --git a/openecomp-ui/resources/scss/bootstrap/_thumbnails.scss b/openecomp-ui/resources/scss/bootstrap/_thumbnails.scss new file mode 100644 index 0000000000..ec4a9cbf97 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_thumbnails.scss @@ -0,0 +1,37 @@ +// +// Thumbnails +// -------------------------------------------------- + +// Mixin and adjust the regular image class +.thumbnail { + display: block; + padding: $thumbnail-padding; + margin-bottom: $line-height-computed; + line-height: $line-height-base; + background-color: $thumbnail-bg; + border: 1px solid $thumbnail-border; + border-radius: $thumbnail-border-radius; + @include transition(border .2s ease-in-out); + + > img, + a > img { + @include img-responsive; + margin-left: auto; + margin-right: auto; + } + + // [converter] extracted a&:hover, a&:focus, a&.active to a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active + + // Image captions + .caption { + padding: $thumbnail-caption-padding; + color: $thumbnail-caption-color; + } +} + +// Add a hover state for linked versions only +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: $link-color; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_tooltip.scss b/openecomp-ui/resources/scss/bootstrap/_tooltip.scss new file mode 100644 index 0000000000..8a7d8856bc --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_tooltip.scss @@ -0,0 +1,115 @@ +// +// Tooltips +// -------------------------------------------------- + +// Base class +.tooltip { + position: absolute; + z-index: $zindex-tooltip; + display: block; + // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. + // So reset our font and text properties to avoid inheriting weird values. + @include reset-text; + font-size: $font-size-small; + + @include opacity(0); + + &.in { + @include opacity($tooltip-opacity); + } + &.top { + margin-top: -3px; + padding: $tooltip-arrow-width 0; + } + &.right { + margin-left: 3px; + padding: 0 $tooltip-arrow-width; + } + &.bottom { + margin-top: 3px; + padding: $tooltip-arrow-width 0; + } + &.left { + margin-left: -3px; + padding: 0 $tooltip-arrow-width; + } +} + +// Wrapper for the tooltip content +.tooltip-inner { + max-width: $tooltip-max-width; + padding: 3px 8px; + color: $tooltip-color; + text-align: center; + background-color: $tooltip-bg; + border-radius: $border-radius-base; +} + +// Arrows +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1 +.tooltip { + &.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -$tooltip-arrow-width; + border-width: $tooltip-arrow-width $tooltip-arrow-width 0; + border-top-color: $tooltip-arrow-color; + } + &.top-left .tooltip-arrow { + bottom: 0; + right: $tooltip-arrow-width; + margin-bottom: -$tooltip-arrow-width; + border-width: $tooltip-arrow-width $tooltip-arrow-width 0; + border-top-color: $tooltip-arrow-color; + } + &.top-right .tooltip-arrow { + bottom: 0; + left: $tooltip-arrow-width; + margin-bottom: -$tooltip-arrow-width; + border-width: $tooltip-arrow-width $tooltip-arrow-width 0; + border-top-color: $tooltip-arrow-color; + } + &.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -$tooltip-arrow-width; + border-width: $tooltip-arrow-width $tooltip-arrow-width $tooltip-arrow-width 0; + border-right-color: $tooltip-arrow-color; + } + &.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -$tooltip-arrow-width; + border-width: $tooltip-arrow-width 0 $tooltip-arrow-width $tooltip-arrow-width; + border-left-color: $tooltip-arrow-color; + } + &.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -$tooltip-arrow-width; + border-width: 0 $tooltip-arrow-width $tooltip-arrow-width; + border-bottom-color: $tooltip-arrow-color; + } + &.bottom-left .tooltip-arrow { + top: 0; + right: $tooltip-arrow-width; + margin-top: -$tooltip-arrow-width; + border-width: 0 $tooltip-arrow-width $tooltip-arrow-width; + border-bottom-color: $tooltip-arrow-color; + } + &.bottom-right .tooltip-arrow { + top: 0; + left: $tooltip-arrow-width; + margin-top: -$tooltip-arrow-width; + border-width: 0 $tooltip-arrow-width $tooltip-arrow-width; + border-bottom-color: $tooltip-arrow-color; + } +} diff --git a/openecomp-ui/resources/scss/bootstrap/_type.scss b/openecomp-ui/resources/scss/bootstrap/_type.scss new file mode 100644 index 0000000000..8fb97e4798 --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_type.scss @@ -0,0 +1,339 @@ +// +// Typography +// -------------------------------------------------- + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + font-family: $headings-font-family; + font-weight: $headings-font-weight; + line-height: $headings-line-height; + color: $headings-color; + + small, + .small { + font-weight: normal; + line-height: 1; + color: $headings-small-color; + } +} + +h1, .h1, +h2, .h2, +h3, .h3 { + margin-top: $line-height-computed; + margin-bottom: ($line-height-computed / 2); + + small, + .small { + font-size: 65%; + } +} + +h4, .h4, +h5, .h5, +h6, .h6 { + margin-top: ($line-height-computed / 2); + margin-bottom: ($line-height-computed / 2); + + small, + .small { + font-size: 75%; + } +} + +h1, .h1 { + font-size: $font-size-h1; +} + +h2, .h2 { + font-size: $font-size-h2; +} + +h3, .h3 { + font-size: $font-size-h3; +} + +h4, .h4 { + font-size: $font-size-h4; +} + +h5, .h5 { + font-size: $font-size-h5; +} + +h6, .h6 { + font-size: $font-size-h6; +} + +// Body text +// ------------------------- + +p { + margin: 0 0 ($line-height-computed / 2); +} + +.lead { + margin-bottom: $line-height-computed; + font-size: floor(($font-size-base * 1.15)); + font-weight: 300; + line-height: 1.4; + + @media (min-width: $screen-sm-min) { + font-size: ($font-size-base * 1.5); + } +} + +// Emphasis & misc +// ------------------------- + +// Ex: (12px small font / 14px base font) * 100% = about 85% +small, +.small { + font-size: floor((100% * $font-size-small / $font-size-base)); +} + +mark, +.mark { + background-color: $state-warning-bg; + padding: .2em; +} + +// Alignment +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +.text-justify { + text-align: justify; +} + +.text-nowrap { + white-space: nowrap; +} + +// Transformation +.text-lowercase { + text-transform: lowercase; +} + +.text-uppercase { + text-transform: uppercase; +} + +.text-capitalize { + text-transform: capitalize; +} + +// Contextual colors +.text-muted { + color: $text-muted; +} + +@include text-emphasis-variant('.text-primary', $brand-primary); + +@include text-emphasis-variant('.text-success', $state-success-text); + +@include text-emphasis-variant('.text-info', $state-info-text); + +@include text-emphasis-variant('.text-warning', $state-warning-text); + +@include text-emphasis-variant('.text-danger', $state-danger-text); + +// Contextual backgrounds +// For now we'll leave these alongside the text classes until v4 when we can +// safely shift things around (per SemVer rules). +.bg-primary { + // Given the contrast here, this is the only class to have its color inverted + // automatically. + color: #fff; +} + +@include bg-variant('.bg-primary', $brand-primary); + +@include bg-variant('.bg-success', $state-success-bg); + +@include bg-variant('.bg-info', $state-info-bg); + +@include bg-variant('.bg-warning', $state-warning-bg); + +@include bg-variant('.bg-danger', $state-danger-bg); + +// Page header +// ------------------------- + +.page-header { + padding-bottom: (($line-height-computed / 2) - 1); + margin: ($line-height-computed * 2) 0 $line-height-computed; + border-bottom: 1px solid $page-header-border-color; +} + +// Lists +// ------------------------- + +// Unordered and Ordered lists +ul, +ol { + margin-top: 0; + margin-bottom: ($line-height-computed / 2); + ul, + ol { + margin-bottom: 0; + } +} + +// List options + +// [converter] extracted from `.list-unstyled` for libsass compatibility +@mixin list-unstyled { + padding-left: 0; + list-style: none; +} + +// [converter] extracted as `@mixin list-unstyled` for libsass compatibility +.list-unstyled { + @include list-unstyled; +} + +// Inline turns list items into inline-block +.list-inline { + @include list-unstyled; + margin-left: -5px; + + > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-top: 0; // Remove browser default + margin-bottom: $line-height-computed; +} + +dt, +dd { + line-height: $line-height-base; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 0; // Undo browser default +} + +// Horizontal description lists +// +// Defaults to being stacked without any of the below styles applied, until the +// grid breakpoint is reached (default of ~768px). + +.dl-horizontal { + dd { + @include clearfix; // Clear the floated `dt` if an empty `dd` is present + } + + @media (min-width: $grid-float-breakpoint) { + dt { + float: left; + width: ($dl-horizontal-offset - 20); + clear: left; + text-align: right; + @include text-overflow; + } + dd { + margin-left: $dl-horizontal-offset; + } + } +} + +// Misc +// ------------------------- + +// Abbreviations and acronyms +abbr[title], + // Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted $abbr-border-color; +} + +.initialism { + font-size: 90%; + @extend .text-uppercase; +} + +// Blockquotes +blockquote { + padding: ($line-height-computed / 2) $line-height-computed; + margin: 0 0 $line-height-computed; + font-size: $blockquote-font-size; + border-left: 5px solid $blockquote-border-color; + + p, + ul, + ol { + &:last-child { + margin-bottom: 0; + } + } + + // Note: Deprecated small and .small as of v3.1.0 + // Context: https://github.com/twbs/bootstrap/issues/11660 + footer, + small, + .small { + display: block; + font-size: 80%; // back to default font-size + line-height: $line-height-base; + color: $blockquote-small-color; + + &:before { + content: '\2014 \00A0'; // em dash, nbsp + } + } +} + +// Opposite alignment of blockquote +// +// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0. +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid $blockquote-border-color; + border-left: 0; + text-align: right; + + // Account for citation + footer, + small, + .small { + &:before { + content: ''; + } + &:after { + content: '\00A0 \2014'; // nbsp, em dash + } + } +} + +// Addresses +address { + margin-bottom: $line-height-computed; + font-style: normal; + line-height: $line-height-base; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_utilities.scss b/openecomp-ui/resources/scss/bootstrap/_utilities.scss new file mode 100644 index 0000000000..137720136b --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_utilities.scss @@ -0,0 +1,57 @@ +// +// Utility classes +// -------------------------------------------------- + +// Floats +// ------------------------- + +.clearfix { + @include clearfix; +} + +.center-block { + @include center-block; +} + +.pull-right { + float: right !important; +} + +.pull-left { + float: left !important; +} + +// Toggling content +// ------------------------- + +// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 +.hide { + display: none !important; +} + +.show { + display: block !important; +} + +.invisible { + visibility: hidden; +} + +.text-hide { + @include text-hide; +} + +// Hide from screenreaders and browsers +// +// Credit: HTML5 Boilerplate + +.hidden { + display: none !important; +} + +// For Affix plugin +// ------------------------- + +.affix { + position: fixed; +} diff --git a/openecomp-ui/resources/scss/bootstrap/_variables.scss b/openecomp-ui/resources/scss/bootstrap/_variables.scss new file mode 100644 index 0000000000..50b8781a3d --- /dev/null +++ b/openecomp-ui/resources/scss/bootstrap/_variables.scss @@ -0,0 +1,852 @@ +$bootstrap-sass-asset-helper: false !default; +// +// Variables +// -------------------------------------------------- + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +$gray-base: #000 !default; +$gray-darker: lighten($gray-base, 13.5%) !default; +// #222 +$gray-dark: lighten($gray-base, 20%) !default; +// #333 +$gray: lighten($gray-base, 33.5%) !default; +// #555 +$gray-light: lighten($gray-base, 46.7%) !default; +// #777 +$gray-lighter: lighten($gray-base, 93.5%) !default; +// #eee + +$brand-primary: darken(#428bca, 6.5%) !default; +// #337ab7 +$brand-success: #5cb85c !default; +$brand-info: #5bc0de !default; +$brand-warning: #f0ad4e !default; +$brand-danger: #d9534f !default; + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for ``. +$body-bg: #fff !default; +//** Global text color on ``. +$text-color: $gray-dark !default; + +//** Global textual link color. +$link-color: $brand-primary !default; +//** Link hover color set via `darken()` function. +$link-hover-color: darken($link-color, 15%) !default; +//** Link hover decoration. +$link-hover-decoration: underline !default; + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; +$font-family-serif: Georgia, "Times New Roman", Times, serif !default; +//** Default monospace fonts for ``, ``, and `
      `.
      +$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default;
      +$font-family-base: $font-family-sans-serif !default;
      +
      +$font-size-base: 14px !default;
      +$font-size-large: ceil(($font-size-base * 1.25)) !default;
      +// ~18px
      +$font-size-small: ceil(($font-size-base * 0.85)) !default;
      +// ~12px
      +
      +$font-size-h1: floor(($font-size-base * 2.6)) !default;
      +// ~36px
      +$font-size-h2: floor(($font-size-base * 2.15)) !default;
      +// ~30px
      +$font-size-h3: ceil(($font-size-base * 1.7)) !default;
      +// ~24px
      +$font-size-h4: ceil(($font-size-base * 1.25)) !default;
      +// ~18px
      +$font-size-h5: $font-size-base !default;
      +$font-size-h6: ceil(($font-size-base * 0.85)) !default;
      +// ~12px
      +
      +//** Unit-less `line-height` for use in components like buttons.
      +$line-height-base: 1.428571429 !default;
      +// 20/14
      +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
      +$line-height-computed: floor(($font-size-base * $line-height-base)) !default;
      +// ~20px
      +
      +//** By default, this inherits from the ``.
      +$headings-font-family: inherit !default;
      +$headings-font-weight: 500 !default;
      +$headings-line-height: 1.1 !default;
      +$headings-color: inherit !default;
      +
      +//== Iconography
      +//
      +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
      +
      +//** Load fonts from this directory.
      +
      +// [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
      +// [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
      +$icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/") !default;
      +
      +//** File name for all font files.
      +$icon-font-name: "glyphicons-halflings-regular" !default;
      +//** Element ID within SVG icon file.
      +$icon-font-svg-id: "glyphicons_halflingsregular" !default;
      +
      +//== Components
      +//
      +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
      +
      +$padding-base-vertical: 6px !default;
      +$padding-base-horizontal: 12px !default;
      +
      +$padding-large-vertical: 10px !default;
      +$padding-large-horizontal: 16px !default;
      +
      +$padding-small-vertical: 5px !default;
      +$padding-small-horizontal: 10px !default;
      +
      +$padding-xs-vertical: 1px !default;
      +$padding-xs-horizontal: 5px !default;
      +
      +$line-height-large: 1.3333333 !default;
      +// extra decimals for Win 8.1 Chrome
      +$line-height-small: 1.5 !default;
      +
      +$border-radius-base: 4px !default;
      +$border-radius-large: 6px !default;
      +$border-radius-small: 3px !default;
      +
      +//** Global color for active items (e.g., navs or dropdowns).
      +$component-active-color: #fff !default;
      +//** Global background color for active items (e.g., navs or dropdowns).
      +$component-active-bg: $brand-primary !default;
      +
      +//** Width of the `border` for generating carets that indicator dropdowns.
      +$caret-width-base: 4px !default;
      +//** Carets increase slightly in size for larger components.
      +$caret-width-large: 5px !default;
      +
      +//== Tables
      +//
      +//## Customizes the `.table` component with basic values, each used across all table variations.
      +
      +//** Padding for ``s and ``s.
      +$table-cell-padding: 8px !default;
      +//** Padding for cells in `.table-condensed`.
      +$table-condensed-cell-padding: 5px !default;
      +
      +//** Default background color used for all tables.
      +$table-bg: transparent !default;
      +//** Background color used for `.table-striped`.
      +$table-bg-accent: #f9f9f9 !default;
      +//** Background color used for `.table-hover`.
      +$table-bg-hover: #f5f5f5 !default;
      +$table-bg-active: $table-bg-hover !default;
      +
      +//** Border color for table and cell borders.
      +$table-border-color: #ddd !default;
      +
      +//== Buttons
      +//
      +//## For each of Bootstrap's buttons, define text, background and border color.
      +
      +$btn-font-weight: normal !default;
      +
      +$btn-default-color: #333 !default;
      +$btn-default-bg: #fff !default;
      +$btn-default-border: #ccc !default;
      +
      +$btn-primary-color: #fff !default;
      +$btn-primary-bg: $brand-primary !default;
      +$btn-primary-border: darken($btn-primary-bg, 5%) !default;
      +
      +$btn-success-color: #fff !default;
      +$btn-success-bg: $brand-success !default;
      +$btn-success-border: darken($btn-success-bg, 5%) !default;
      +
      +$btn-info-color: #fff !default;
      +$btn-info-bg: $brand-info !default;
      +$btn-info-border: darken($btn-info-bg, 5%) !default;
      +
      +$btn-warning-color: #fff !default;
      +$btn-warning-bg: $brand-warning !default;
      +$btn-warning-border: darken($btn-warning-bg, 5%) !default;
      +
      +$btn-danger-color: #fff !default;
      +$btn-danger-bg: $brand-danger !default;
      +$btn-danger-border: darken($btn-danger-bg, 5%) !default;
      +
      +$btn-link-disabled-color: $gray-light !default;
      +
      +// Allows for customizing button radius independently from global border radius
      +$btn-border-radius-base: $border-radius-base !default;
      +$btn-border-radius-large: $border-radius-large !default;
      +$btn-border-radius-small: $border-radius-small !default;
      +
      +//== Forms
      +//
      +//##
      +
      +//** `` background color
      +$input-bg: #fff !default;
      +//** `` background color
      +$input-bg-disabled: $gray-lighter !default;
      +
      +//** Text color for ``s
      +$input-color: $gray !default;
      +//** `` border color
      +$input-border: #ccc !default;
      +
      +// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
      +//** Default `.form-control` border radius
      +// This has no effect on ``s in CSS.
      +$input-border-radius: $border-radius-base !default;
      +//** Large `.form-control` border radius
      +$input-border-radius-large: $border-radius-large !default;
      +//** Small `.form-control` border radius
      +$input-border-radius-small: $border-radius-small !default;
      +
      +//** Border color for inputs on focus
      +$input-border-focus: #66afe9 !default;
      +
      +//** Placeholder text color
      +$input-color-placeholder: #999 !default;
      +
      +//** Default `.form-control` height
      +$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
      +//** Large `.form-control` height
      +$input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
      +//** Small `.form-control` height
      +$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
      +
      +//** `.form-group` margin
      +$form-group-margin-bottom: 15px !default;
      +
      +$legend-color: $gray-dark !default;
      +$legend-border-color: #e5e5e5 !default;
      +
      +//** Background color for textual input addons
      +$input-group-addon-bg: $gray-lighter !default;
      +//** Border color for textual input addons
      +$input-group-addon-border-color: $input-border !default;
      +
      +//** Disabled cursor for form controls and buttons.
      +$cursor-disabled: not-allowed !default;
      +
      +//== Dropdowns
      +//
      +//## Dropdown menu container and contents.
      +
      +//** Background for the dropdown menu.
      +$dropdown-bg: #fff !default;
      +//** Dropdown menu `border-color`.
      +$dropdown-border: rgba(0, 0, 0, .15) !default;
      +//** Dropdown menu `border-color` **for IE8**.
      +$dropdown-fallback-border: #ccc !default;
      +//** Divider color for between dropdown items.
      +$dropdown-divider-bg: #e5e5e5 !default;
      +
      +//** Dropdown link text color.
      +$dropdown-link-color: $gray-dark !default;
      +//** Hover color for dropdown links.
      +$dropdown-link-hover-color: darken($gray-dark, 5%) !default;
      +//** Hover background for dropdown links.
      +$dropdown-link-hover-bg: #f5f5f5 !default;
      +
      +//** Active dropdown menu item text color.
      +$dropdown-link-active-color: $component-active-color !default;
      +//** Active dropdown menu item background color.
      +$dropdown-link-active-bg: $component-active-bg !default;
      +
      +//** Disabled dropdown menu item background color.
      +$dropdown-link-disabled-color: $gray-light !default;
      +
      +//** Text color for headers within dropdown menus.
      +$dropdown-header-color: $gray-light !default;
      +
      +//** Deprecated `$dropdown-caret-color` as of v3.1.0
      +$dropdown-caret-color: #000 !default;
      +
      +//-- Z-index master list
      +//
      +// Warning: Avoid customizing these values. They're used for a bird's eye view
      +// of components dependent on the z-axis and are designed to all work together.
      +//
      +// Note: These variables are not generated into the Customizer.
      +
      +$zindex-navbar: 1000 !default;
      +$zindex-dropdown: 1000 !default;
      +$zindex-popover: 1060 !default;
      +$zindex-tooltip: 1070 !default;
      +$zindex-navbar-fixed: 1030 !default;
      +$zindex-modal-background: 1040 !default;
      +$zindex-modal: 1050 !default;
      +
      +//== Media queries breakpoints
      +//
      +//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
      +
      +// Extra small screen / phone
      +//** Deprecated `$screen-xs` as of v3.0.1
      +$screen-xs: 480px !default;
      +//** Deprecated `$screen-xs-min` as of v3.2.0
      +$screen-xs-min: $screen-xs !default;
      +//** Deprecated `$screen-phone` as of v3.0.1
      +$screen-phone: $screen-xs-min !default;
      +
      +// Small screen / tablet
      +//** Deprecated `$screen-sm` as of v3.0.1
      +$screen-sm: 768px !default;
      +$screen-sm-min: $screen-sm !default;
      +//** Deprecated `$screen-tablet` as of v3.0.1
      +$screen-tablet: $screen-sm-min !default;
      +
      +// Medium screen / desktop
      +//** Deprecated `$screen-md` as of v3.0.1
      +$screen-md: 992px !default;
      +$screen-md-min: $screen-md !default;
      +//** Deprecated `$screen-desktop` as of v3.0.1
      +$screen-desktop: $screen-md-min !default;
      +
      +// Large screen / wide desktop
      +//** Deprecated `$screen-lg` as of v3.0.1
      +$screen-lg: 1200px !default;
      +$screen-lg-min: $screen-lg !default;
      +//** Deprecated `$screen-lg-desktop` as of v3.0.1
      +$screen-lg-desktop: $screen-lg-min !default;
      +
      +// So media queries don't overlap when required, provide a maximum
      +$screen-xs-max: ($screen-sm-min - 1) !default;
      +$screen-sm-max: ($screen-md-min - 1) !default;
      +$screen-md-max: ($screen-lg-min - 1) !default;
      +
      +//== Grid system
      +//
      +//## Define your custom responsive grid.
      +
      +//** Number of columns in the grid.
      +$grid-columns: 12 !default;
      +//** Padding between columns. Gets divided in half for the left and right.
      +$grid-gutter-width: 30px !default;
      +// Navbar collapse
      +//** Point at which the navbar becomes uncollapsed.
      +$grid-float-breakpoint: $screen-sm-min !default;
      +//** Point at which the navbar begins collapsing.
      +$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
      +
      +//== Container sizes
      +//
      +//## Define the maximum width of `.container` for different screen sizes.
      +
      +// Small screen / tablet
      +$container-tablet: (720px + $grid-gutter-width) !default;
      +//** For `$screen-sm-min` and up.
      +$container-sm: $container-tablet !default;
      +
      +// Medium screen / desktop
      +$container-desktop: (940px + $grid-gutter-width) !default;
      +//** For `$screen-md-min` and up.
      +$container-md: $container-desktop !default;
      +
      +// Large screen / wide desktop
      +$container-large-desktop: (1140px + $grid-gutter-width) !default;
      +//** For `$screen-lg-min` and up.
      +$container-lg: $container-large-desktop !default;
      +
      +//== Navbar
      +//
      +//##
      +
      +// Basics of a navbar
      +$navbar-height: 50px !default;
      +$navbar-margin-bottom: $line-height-computed !default;
      +$navbar-border-radius: $border-radius-base !default;
      +$navbar-padding-horizontal: floor(($grid-gutter-width / 2)) !default;
      +$navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default;
      +$navbar-collapse-max-height: 340px !default;
      +
      +$navbar-default-color: #777 !default;
      +$navbar-default-bg: #f8f8f8 !default;
      +$navbar-default-border: darken($navbar-default-bg, 6.5%) !default;
      +
      +// Navbar links
      +$navbar-default-link-color: #777 !default;
      +$navbar-default-link-hover-color: #333 !default;
      +$navbar-default-link-hover-bg: transparent !default;
      +$navbar-default-link-active-color: #555 !default;
      +$navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%) !default;
      +$navbar-default-link-disabled-color: #ccc !default;
      +$navbar-default-link-disabled-bg: transparent !default;
      +
      +// Navbar brand label
      +$navbar-default-brand-color: $navbar-default-link-color !default;
      +$navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) !default;
      +$navbar-default-brand-hover-bg: transparent !default;
      +
      +// Navbar toggle
      +$navbar-default-toggle-hover-bg: #ddd !default;
      +$navbar-default-toggle-icon-bar-bg: #888 !default;
      +$navbar-default-toggle-border-color: #ddd !default;
      +
      +//=== Inverted navbar
      +// Reset inverted navbar basics
      +$navbar-inverse-color: lighten($gray-light, 15%) !default;
      +$navbar-inverse-bg: #222 !default;
      +$navbar-inverse-border: darken($navbar-inverse-bg, 10%) !default;
      +
      +// Inverted navbar links
      +$navbar-inverse-link-color: lighten($gray-light, 15%) !default;
      +$navbar-inverse-link-hover-color: #fff !default;
      +$navbar-inverse-link-hover-bg: transparent !default;
      +$navbar-inverse-link-active-color: $navbar-inverse-link-hover-color !default;
      +$navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%) !default;
      +$navbar-inverse-link-disabled-color: #444 !default;
      +$navbar-inverse-link-disabled-bg: transparent !default;
      +
      +// Inverted navbar brand label
      +$navbar-inverse-brand-color: $navbar-inverse-link-color !default;
      +$navbar-inverse-brand-hover-color: #fff !default;
      +$navbar-inverse-brand-hover-bg: transparent !default;
      +
      +// Inverted navbar toggle
      +$navbar-inverse-toggle-hover-bg: #333 !default;
      +$navbar-inverse-toggle-icon-bar-bg: #fff !default;
      +$navbar-inverse-toggle-border-color: #333 !default;
      +
      +//== Navs
      +//
      +//##
      +
      +//=== Shared nav styles
      +$nav-link-padding: 10px 15px !default;
      +$nav-link-hover-bg: $gray-lighter !default;
      +
      +$nav-disabled-link-color: $gray-light !default;
      +$nav-disabled-link-hover-color: $gray-light !default;
      +
      +//== Tabs
      +$nav-tabs-border-color: #ddd !default;
      +
      +$nav-tabs-link-hover-border-color: $gray-lighter !default;
      +
      +$nav-tabs-active-link-hover-bg: $body-bg !default;
      +$nav-tabs-active-link-hover-color: $gray !default;
      +$nav-tabs-active-link-hover-border-color: #ddd !default;
      +
      +$nav-tabs-justified-link-border-color: #ddd !default;
      +$nav-tabs-justified-active-link-border-color: $body-bg !default;
      +
      +//== Pills
      +$nav-pills-border-radius: $border-radius-base !default;
      +$nav-pills-active-link-hover-bg: $component-active-bg !default;
      +$nav-pills-active-link-hover-color: $component-active-color !default;
      +
      +//== Pagination
      +//
      +//##
      +
      +$pagination-color: $link-color !default;
      +$pagination-bg: #fff !default;
      +$pagination-border: #ddd !default;
      +
      +$pagination-hover-color: $link-hover-color !default;
      +$pagination-hover-bg: $gray-lighter !default;
      +$pagination-hover-border: #ddd !default;
      +
      +$pagination-active-color: #fff !default;
      +$pagination-active-bg: $brand-primary !default;
      +$pagination-active-border: $brand-primary !default;
      +
      +$pagination-disabled-color: $gray-light !default;
      +$pagination-disabled-bg: #fff !default;
      +$pagination-disabled-border: #ddd !default;
      +
      +//== Pager
      +//
      +//##
      +
      +$pager-bg: $pagination-bg !default;
      +$pager-border: $pagination-border !default;
      +$pager-border-radius: 15px !default;
      +
      +$pager-hover-bg: $pagination-hover-bg !default;
      +
      +$pager-active-bg: $pagination-active-bg !default;
      +$pager-active-color: $pagination-active-color !default;
      +
      +$pager-disabled-color: $pagination-disabled-color !default;
      +
      +//== Jumbotron
      +//
      +//##
      +
      +$jumbotron-padding: 30px !default;
      +$jumbotron-color: inherit !default;
      +$jumbotron-bg: $gray-lighter !default;
      +$jumbotron-heading-color: inherit !default;
      +$jumbotron-font-size: ceil(($font-size-base * 1.5)) !default;
      +$jumbotron-heading-font-size: ceil(($font-size-base * 4.5)) !default;
      +
      +//== Form states and alerts
      +//
      +//## Define colors for form feedback states and, by default, alerts.
      +
      +$state-success-text: #3c763d !default;
      +$state-success-bg: #dff0d8 !default;
      +$state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default;
      +
      +$state-info-text: #31708f !default;
      +$state-info-bg: #d9edf7 !default;
      +$state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default;
      +
      +$state-warning-text: #8a6d3b !default;
      +$state-warning-bg: #fcf8e3 !default;
      +$state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default;
      +
      +$state-danger-text: #a94442 !default;
      +$state-danger-bg: #f2dede !default;
      +$state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default;
      +
      +//== Tooltips
      +//
      +//##
      +
      +//** Tooltip max width
      +$tooltip-max-width: 200px !default;
      +//** Tooltip text color
      +$tooltip-color: #fff !default;
      +//** Tooltip background color
      +$tooltip-bg: #000 !default;
      +$tooltip-opacity: .9 !default;
      +
      +//** Tooltip arrow width
      +$tooltip-arrow-width: 5px !default;
      +//** Tooltip arrow color
      +$tooltip-arrow-color: $tooltip-bg !default;
      +
      +//== Popovers
      +//
      +//##
      +
      +//** Popover body background color
      +$popover-bg: #fff !default;
      +//** Popover maximum width
      +$popover-max-width: 276px !default;
      +//** Popover border color
      +$popover-border-color: rgba(0, 0, 0, .2) !default;
      +//** Popover fallback border color
      +$popover-fallback-border-color: #ccc !default;
      +
      +//** Popover title background color
      +$popover-title-bg: darken($popover-bg, 3%) !default;
      +
      +//** Popover arrow width
      +$popover-arrow-width: 10px !default;
      +//** Popover arrow color
      +$popover-arrow-color: $popover-bg !default;
      +
      +//** Popover outer arrow width
      +$popover-arrow-outer-width: ($popover-arrow-width + 1) !default;
      +//** Popover outer arrow color
      +$popover-arrow-outer-color: fade_in($popover-border-color, 0.05) !default;
      +//** Popover outer arrow fallback color
      +$popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) !default;
      +
      +//== Labels
      +//
      +//##
      +
      +//** Default label background color
      +$label-default-bg: $gray-light !default;
      +//** Primary label background color
      +$label-primary-bg: $brand-primary !default;
      +//** Success label background color
      +$label-success-bg: $brand-success !default;
      +//** Info label background color
      +$label-info-bg: $brand-info !default;
      +//** Warning label background color
      +$label-warning-bg: $brand-warning !default;
      +//** Danger label background color
      +$label-danger-bg: $brand-danger !default;
      +
      +//** Default label text color
      +$label-color: #fff !default;
      +//** Default text color of a linked label
      +$label-link-hover-color: #fff !default;
      +
      +//== Modals
      +//
      +//##
      +
      +//** Padding applied to the modal body
      +$modal-inner-padding: 15px !default;
      +
      +//** Padding applied to the modal title
      +$modal-title-padding: 15px !default;
      +//** Modal title line-height
      +$modal-title-line-height: $line-height-base !default;
      +
      +//** Background color of modal content area
      +$modal-content-bg: #fff !default;
      +//** Modal content border color
      +$modal-content-border-color: rgba(0, 0, 0, .2) !default;
      +//** Modal content border color **for IE8**
      +$modal-content-fallback-border-color: #999 !default;
      +
      +//** Modal backdrop background color
      +$modal-backdrop-bg: #000 !default;
      +//** Modal backdrop opacity
      +$modal-backdrop-opacity: .5 !default;
      +//** Modal header border color
      +$modal-header-border-color: #e5e5e5 !default;
      +//** Modal footer border color
      +$modal-footer-border-color: $modal-header-border-color !default;
      +
      +$modal-lg: 900px !default;
      +$modal-md: 600px !default;
      +$modal-sm: 300px !default;
      +
      +//== Alerts
      +//
      +//## Define alert colors, border radius, and padding.
      +
      +$alert-padding: 15px !default;
      +$alert-border-radius: $border-radius-base !default;
      +$alert-link-font-weight: bold !default;
      +
      +$alert-success-bg: $state-success-bg !default;
      +$alert-success-text: $state-success-text !default;
      +$alert-success-border: $state-success-border !default;
      +
      +$alert-info-bg: $state-info-bg !default;
      +$alert-info-text: $state-info-text !default;
      +$alert-info-border: $state-info-border !default;
      +
      +$alert-warning-bg: $state-warning-bg !default;
      +$alert-warning-text: $state-warning-text !default;
      +$alert-warning-border: $state-warning-border !default;
      +
      +$alert-danger-bg: $state-danger-bg !default;
      +$alert-danger-text: $state-danger-text !default;
      +$alert-danger-border: $state-danger-border !default;
      +
      +//== Progress bars
      +//
      +//##
      +
      +//** Background color of the whole progress component
      +$progress-bg: #f5f5f5 !default;
      +//** Progress bar text color
      +$progress-bar-color: #fff !default;
      +//** Variable for setting rounded corners on progress bar.
      +$progress-border-radius: $border-radius-base !default;
      +
      +//** Default progress bar color
      +$progress-bar-bg: $brand-primary !default;
      +//** Success progress bar color
      +$progress-bar-success-bg: $brand-success !default;
      +//** Warning progress bar color
      +$progress-bar-warning-bg: $brand-warning !default;
      +//** Danger progress bar color
      +$progress-bar-danger-bg: $brand-danger !default;
      +//** Info progress bar color
      +$progress-bar-info-bg: $brand-info !default;
      +
      +//== List group
      +//
      +//##
      +
      +//** Background color on `.list-group-item`
      +$list-group-bg: #fff !default;
      +//** `.list-group-item` border color
      +$list-group-border: #ddd !default;
      +//** List group border radius
      +$list-group-border-radius: $border-radius-base !default;
      +
      +//** Background color of single list items on hover
      +$list-group-hover-bg: #f5f5f5 !default;
      +//** Text color of active list items
      +$list-group-active-color: $component-active-color !default;
      +//** Background color of active list items
      +$list-group-active-bg: $component-active-bg !default;
      +//** Border color of active list elements
      +$list-group-active-border: $list-group-active-bg !default;
      +//** Text color for content within active list items
      +$list-group-active-text-color: lighten($list-group-active-bg, 40%) !default;
      +
      +//** Text color of disabled list items
      +$list-group-disabled-color: $gray-light !default;
      +//** Background color of disabled list items
      +$list-group-disabled-bg: $gray-lighter !default;
      +//** Text color for content within disabled list items
      +$list-group-disabled-text-color: $list-group-disabled-color !default;
      +
      +$list-group-link-color: #555 !default;
      +$list-group-link-hover-color: $list-group-link-color !default;
      +$list-group-link-heading-color: #333 !default;
      +
      +//== Panels
      +//
      +//##
      +
      +$panel-bg: #fff !default;
      +$panel-body-padding: 15px !default;
      +$panel-heading-padding: 10px 15px !default;
      +$panel-footer-padding: $panel-heading-padding !default;
      +$panel-border-radius: $border-radius-base !default;
      +
      +//** Border color for elements within panels
      +$panel-inner-border: #ddd !default;
      +$panel-footer-bg: #f5f5f5 !default;
      +
      +$panel-default-text: $gray-dark !default;
      +$panel-default-border: #ddd !default;
      +$panel-default-heading-bg: #f5f5f5 !default;
      +
      +$panel-primary-text: #fff !default;
      +$panel-primary-border: $brand-primary !default;
      +$panel-primary-heading-bg: $brand-primary !default;
      +
      +$panel-success-text: $state-success-text !default;
      +$panel-success-border: $state-success-border !default;
      +$panel-success-heading-bg: $state-success-bg !default;
      +
      +$panel-info-text: $state-info-text !default;
      +$panel-info-border: $state-info-border !default;
      +$panel-info-heading-bg: $state-info-bg !default;
      +
      +$panel-warning-text: $state-warning-text !default;
      +$panel-warning-border: $state-warning-border !default;
      +$panel-warning-heading-bg: $state-warning-bg !default;
      +
      +$panel-danger-text: $state-danger-text !default;
      +$panel-danger-border: $state-danger-border !default;
      +$panel-danger-heading-bg: $state-danger-bg !default;
      +
      +//== Thumbnails
      +//
      +//##
      +
      +//** Padding around the thumbnail image
      +$thumbnail-padding: 4px !default;
      +//** Thumbnail background color
      +$thumbnail-bg: $body-bg !default;
      +//** Thumbnail border color
      +$thumbnail-border: #ddd !default;
      +//** Thumbnail border radius
      +$thumbnail-border-radius: $border-radius-base !default;
      +
      +//** Custom text color for thumbnail captions
      +$thumbnail-caption-color: $text-color !default;
      +//** Padding around the thumbnail caption
      +$thumbnail-caption-padding: 9px !default;
      +
      +//== Wells
      +//
      +//##
      +
      +$well-bg: #f5f5f5 !default;
      +$well-border: darken($well-bg, 7%) !default;
      +
      +//== Badges
      +//
      +//##
      +
      +$badge-color: #fff !default;
      +//** Linked badge text color on hover
      +$badge-link-hover-color: #fff !default;
      +$badge-bg: $gray-light !default;
      +
      +//** Badge text color in active nav link
      +$badge-active-color: $link-color !default;
      +//** Badge background color in active nav link
      +$badge-active-bg: #fff !default;
      +
      +$badge-font-weight: bold !default;
      +$badge-line-height: 1 !default;
      +$badge-border-radius: 10px !default;
      +
      +//== Breadcrumbs
      +//
      +//##
      +
      +$breadcrumb-padding-vertical: 8px !default;
      +$breadcrumb-padding-horizontal: 15px !default;
      +//** Breadcrumb background color
      +$breadcrumb-bg: #f5f5f5 !default;
      +//** Breadcrumb text color
      +$breadcrumb-color: #ccc !default;
      +//** Text color of current page in the breadcrumb
      +$breadcrumb-active-color: $gray-light !default;
      +//** Textual separator for between breadcrumb elements
      +$breadcrumb-separator: "/" !default;
      +
      +//== Carousel
      +//
      +//##
      +
      +$carousel-text-shadow: 0 1px 2px rgba(0, 0, 0, .6) !default;
      +
      +$carousel-control-color: #fff !default;
      +$carousel-control-width: 15% !default;
      +$carousel-control-opacity: .5 !default;
      +$carousel-control-font-size: 20px !default;
      +
      +$carousel-indicator-active-bg: #fff !default;
      +$carousel-indicator-border-color: #fff !default;
      +
      +$carousel-caption-color: #fff !default;
      +
      +//== Close
      +//
      +//##
      +
      +$close-font-weight: bold !default;
      +$close-color: #000 !default;
      +$close-text-shadow: 0 1px 0 #fff !default;
      +
      +//== Code
      +//
      +//##
      +
      +$code-color: #c7254e !default;
      +$code-bg: #f9f2f4 !default;
      +
      +$kbd-color: #fff !default;
      +$kbd-bg: #333 !default;
      +
      +$pre-bg: #f5f5f5 !default;
      +$pre-color: $gray-dark !default;
      +$pre-border-color: #ccc !default;
      +$pre-scrollable-max-height: 340px !default;
      +
      +//== Type
      +//
      +//##
      +
      +//** Horizontal offset for forms and lists.
      +$component-offset-horizontal: 180px !default;
      +//** Text muted color
      +$text-muted: $gray-light !default;
      +//** Abbreviations and acronyms border color
      +$abbr-border-color: $gray-light !default;
      +//** Headings small color
      +$headings-small-color: $gray-light !default;
      +//** Blockquote small color
      +$blockquote-small-color: $gray-light !default;
      +//** Blockquote font size
      +$blockquote-font-size: ($font-size-base * 1.25) !default;
      +//** Blockquote border color
      +$blockquote-border-color: $gray-lighter !default;
      +//** Page header border color
      +$page-header-border-color: $gray-lighter !default;
      +//** Width of horizontal description list titles
      +$dl-horizontal-offset: $component-offset-horizontal !default;
      +//** Horizontal line color.
      +$hr-border: $gray-lighter !default;
      diff --git a/openecomp-ui/resources/scss/bootstrap/_wells.scss b/openecomp-ui/resources/scss/bootstrap/_wells.scss
      new file mode 100644
      index 0000000000..8d13cb67d3
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/_wells.scss
      @@ -0,0 +1,29 @@
      +//
      +// Wells
      +// --------------------------------------------------
      +
      +// Base class
      +.well {
      +  min-height: 20px;
      +  padding: 19px;
      +  margin-bottom: 20px;
      +  background-color: $well-bg;
      +  border: 1px solid $well-border;
      +  border-radius: $border-radius-base;
      +  @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .05));
      +  blockquote {
      +	border-color: #ddd;
      +	border-color: rgba(0, 0, 0, .15);
      +  }
      +}
      +
      +// Sizes
      +.well-lg {
      +  padding: 24px;
      +  border-radius: $border-radius-large;
      +}
      +
      +.well-sm {
      +  padding: 9px;
      +  border-radius: $border-radius-small;
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_alerts.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_alerts.scss
      new file mode 100644
      index 0000000000..b092e85eb4
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_alerts.scss
      @@ -0,0 +1,14 @@
      +// Alerts
      +
      +@mixin alert-variant($background, $border, $text-color) {
      +  background-color: $background;
      +  border-color: $border;
      +  color: $text-color;
      +
      +  hr {
      +	border-top-color: darken($border, 5%);
      +  }
      +  .alert-link {
      +	color: darken($text-color, 10%);
      +  }
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_background-variant.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_background-variant.scss
      new file mode 100644
      index 0000000000..533ff12c84
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_background-variant.scss
      @@ -0,0 +1,12 @@
      +// Contextual backgrounds
      +
      +// [converter] $parent hack
      +@mixin bg-variant($parent, $color) {
      +  #{$parent} {
      +	background-color: $color;
      +  }
      +  a#{$parent}:hover,
      +  a#{$parent}:focus {
      +	background-color: darken($color, 10%);
      +  }
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_border-radius.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_border-radius.scss
      new file mode 100644
      index 0000000000..30e065daf8
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_border-radius.scss
      @@ -0,0 +1,21 @@
      +// Single side border-radius
      +
      +@mixin border-top-radius($radius) {
      +  border-top-right-radius: $radius;
      +  border-top-left-radius: $radius;
      +}
      +
      +@mixin border-right-radius($radius) {
      +  border-bottom-right-radius: $radius;
      +  border-top-right-radius: $radius;
      +}
      +
      +@mixin border-bottom-radius($radius) {
      +  border-bottom-right-radius: $radius;
      +  border-bottom-left-radius: $radius;
      +}
      +
      +@mixin border-left-radius($radius) {
      +  border-bottom-left-radius: $radius;
      +  border-top-left-radius: $radius;
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_buttons.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_buttons.scss
      new file mode 100644
      index 0000000000..255c259ddb
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_buttons.scss
      @@ -0,0 +1,68 @@
      +// Button variants
      +//
      +// Easily pump out default styles, as well as :hover, :focus, :active,
      +// and disabled options for all buttons
      +
      +@mixin button-variant($color, $background, $border) {
      +  color: $color;
      +  background-color: $background;
      +  border-color: $border;
      +
      +  &:focus,
      +  &.focus {
      +	color: $color;
      +	background-color: darken($background, 10%);
      +	border-color: darken($border, 25%);
      +  }
      +  &:hover {
      +	color: $color;
      +	background-color: darken($background, 10%);
      +	border-color: darken($border, 12%);
      +  }
      +  &:active,
      +  &.active,
      +  .open > &.dropdown-toggle {
      +	color: $color;
      +	background-color: darken($background, 10%);
      +	border-color: darken($border, 12%);
      +
      +	&:hover,
      +	&:focus,
      +	&.focus {
      +	  color: $color;
      +	  background-color: darken($background, 17%);
      +	  border-color: darken($border, 25%);
      +	}
      +  }
      +  &:active,
      +  &.active,
      +  .open > &.dropdown-toggle {
      +	background-image: none;
      +  }
      +  &.disabled,
      +  &[disabled],
      +  fieldset[disabled] & {
      +	&,
      +	&:hover,
      +	&:focus,
      +	&.focus,
      +	&:active,
      +	&.active {
      +	  background-color: $background;
      +	  border-color: $border;
      +	}
      +  }
      +
      +  .badge {
      +	color: $background;
      +	background-color: $color;
      +  }
      +}
      +
      +// Button sizes
      +@mixin button-size($padding-vertical, $padding-horizontal, $font-size, $line-height, $border-radius) {
      +  padding: $padding-vertical $padding-horizontal;
      +  font-size: $font-size;
      +  line-height: $line-height;
      +  border-radius: $border-radius;
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_center-block.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_center-block.scss
      new file mode 100644
      index 0000000000..e06fb5e276
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_center-block.scss
      @@ -0,0 +1,7 @@
      +// Center-align a block level element
      +
      +@mixin center-block() {
      +  display: block;
      +  margin-left: auto;
      +  margin-right: auto;
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_clearfix.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_clearfix.scss
      new file mode 100644
      index 0000000000..8042d1823b
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_clearfix.scss
      @@ -0,0 +1,22 @@
      +// Clearfix
      +//
      +// For modern browsers
      +// 1. The space content is one way to avoid an Opera bug when the
      +//    contenteditable attribute is included anywhere else in the document.
      +//    Otherwise it causes space to appear at the top and bottom of elements
      +//    that are clearfixed.
      +// 2. The use of `table` rather than `block` is only necessary if using
      +//    `:before` to contain the top-margins of child elements.
      +//
      +// Source: http://nicolasgallagher.com/micro-clearfix-hack/
      +
      +@mixin clearfix() {
      +  &:before,
      +  &:after {
      +	content: " "; // 1
      +	display: table; // 2
      +  }
      +  &:after {
      +	clear: both;
      +  }
      +}
      diff --git a/openecomp-ui/resources/scss/bootstrap/mixins/_forms.scss b/openecomp-ui/resources/scss/bootstrap/mixins/_forms.scss
      new file mode 100644
      index 0000000000..7a26de5515
      --- /dev/null
      +++ b/openecomp-ui/resources/scss/bootstrap/mixins/_forms.scss
      @@ -0,0 +1,87 @@
      +// Form validation states
      +//
      +// Used in forms.less to generate the form validation CSS for warnings, errors,
      +// and successes.
      +
      +@mixin form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) {
      +  // Color the label and help text
      +  .help-block,
      +  .control-label,
      +  .radio,
      +  .checkbox,
      +  .radio-inline,
      +  .checkbox-inline,
      +  &.radio label,
      +  &.checkbox label,
      +  &.radio-inline label,
      +  &.checkbox-inline label {
      +	color: $text-color;
      +  }
      +  // Set the border and box shadow on specific inputs to match
      +  .form-control {
      +	border-color: $border-color;
      +	@include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075)); // Redeclare so transitions work
      +	&:focus {
      +	  border-color: darken($border-color, 10%);
      +	  $shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px lighten($border-color, 20%);
      +	  @include box-shadow($shadow);
      +	}
      +  }
      +  // Set validation states also for addons
      +  .input-group-addon {
      +	color: $text-color;
      +	border-color: $border-color;
      +	background-color: $background-color;
      +  }
      +  // Optional feedback icon
      +  .form-control-feedback {
      +	color: $text-color;
      +  }
      +}
      +
      +// Form control focus state
      +//
      +// Generate a customized focus state and for any input with the specified color,
      +// which defaults to the `$input-border-focus` variable.
      +//
      +// We highly encourage you to not customize the default value, but instead use
      +// this to tweak colors on an as-needed basis. This aesthetic change is based on
      +// WebKit's default styles, but applicable to a wider range of browsers. Its
      +// usability and accessibility should be taken into account with any change.
      +//
      +// Example usage: change the default blue border and shadow to white for better
      +// contrast against a dark gray background.
      +@mixin form-control-focus($color: $input-border-focus) {
      +  $color-rgba: rgba(red($color), green($color), blue($color), .6);
      +  &:focus {
      +	border-color: $color;
      +	outline: 0;
      +	@include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px $color-rgba);
      +  }
      +}
      +
      +// Form control sizing
      +//
      +// Relative text size, padding, and border-radii changes for form controls. For
      +// horizontal sizing, wrap controls in the predefined grid classes. ` this.searchInputNode = input}
      +					className={inputClasses}
      +					groupClassName='expandable-input-control'
      +					onChange={e => this.handleInput(e)}
      +					onFocus={this.handleFocus}/>
      +				{this.state.showInput && this.state.value && }
      +				{!this.state.value && }
      +			
      + ); + } +} + +export default ExpandableInput; diff --git a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx new file mode 100644 index 0000000000..1036ac41c3 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx @@ -0,0 +1,52 @@ +/** + * 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 Select from 'react-select'; + +class SelectInput extends Component { + + inputValue = []; + + render() { + let {label, value, ...other} = this.props; + return ( +
      +
      + {label && } + + +
      +
      + ); + } + + click = () => { + let value = !this.state.value; + this.setState({value}); + + let onChange = this.props.onChange; + if (onChange) { + onChange(value); + } + } + + getValue() { + return this.state.value; + } +} diff --git a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx new file mode 100644 index 0000000000..171bead9bb --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx @@ -0,0 +1,132 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import Input from 'react-bootstrap/lib/Input.js'; + +class DualListboxView extends React.Component { + + static propTypes = { + + availableList: React.PropTypes.arrayOf(React.PropTypes.shape({ + id: React.PropTypes.string.isRequired, + name: React.PropTypes.string.isRequired + })), + filterTitle: React.PropTypes.shape({ + left: React.PropTypes.string, + right: React.PropTypes.string + }), + selectedValuesList: React.PropTypes.arrayOf(React.PropTypes.string), + + onChange: React.PropTypes.func.isRequired + }; + + static defaultProps = { + selectedValuesList: [], + availableList: [], + filterTitle: { + left: '', + right: '' + } + }; + + state = { + availableListFilter: '', + selectedValuesListFilter: '' + }; + + static contextTypes = { + isReadOnlyMode: React.PropTypes.bool + }; + + render() { + let {availableList, selectedValuesList, filterTitle} = this.props; + let {availableListFilter, selectedValuesListFilter} = this.state; + let isReadOnlyMode = this.context.isReadOnlyMode; + + let unselectedList = availableList.filter(availableItem => !selectedValuesList.find(value => value === availableItem.id)); + let selectedList = availableList.filter(availableItem => selectedValuesList.find(value => value === availableItem.id)); + selectedList = selectedList.sort((a, b) => selectedValuesList.indexOf(a.id) - selectedValuesList.indexOf(b.id)); + + return ( +
      + {this.renderListbox(filterTitle.left, unselectedList, { + value: availableListFilter, + ref: 'availableListFilter', + disabled: isReadOnlyMode, + onChange: () => this.setState({availableListFilter: this.refs.availableListFilter.getValue()}) + }, {ref: 'availableValues', disabled: isReadOnlyMode})} + {this.renderOperationsBar(isReadOnlyMode)} + {this.renderListbox(filterTitle.right, selectedList, { + value: selectedValuesListFilter, + ref: 'selectedValuesListFilter', + disabled: isReadOnlyMode, + onChange: () => this.setState({selectedValuesListFilter: this.refs.selectedValuesListFilter.getValue()}) + }, {ref: 'selectedValues', disabled: isReadOnlyMode})} +
      + ); + } + + renderListbox(filterTitle, list, filterProps, props) { + let regExFilter = new RegExp(escape(filterProps.value), 'i'); + let matchedItems = list.filter(item => item.name.match(regExFilter)); + let unMatchedItems = list.filter(item => !item.name.match(regExFilter)); + + + return ( +
      +

      {filterTitle}

      +
      + + +
      + + {matchedItems.map(item => this.renderOption(item.id, item.name))} + {matchedItems.length && unMatchedItems.length && } + {unMatchedItems.map(item => this.renderOption(item.id, item.name))} + +
      + ); + } + + renderOption(value, name) { + return (); + } + + renderOperationsBar(isReadOnlyMode) { + return ( +
      + {this.renderOperationBarButton(() => this.addToSelectedList(), 'angle-right')} + {this.renderOperationBarButton(() => this.removeFromSelectedList(), 'angle-left')} + {this.renderOperationBarButton(() => this.addAllToSelectedList(), 'angle-double-right')} + {this.renderOperationBarButton(() => this.removeAllFromSelectedList(), 'angle-double-left')} +
      + ); + } + + renderOperationBarButton(onClick, fontAwesomeIconName){ + return (
      ); + } + + addToSelectedList() { + this.props.onChange(this.props.selectedValuesList.concat(this.refs.availableValues.getValue())); + } + + removeFromSelectedList() { + const selectedValues = this.refs.selectedValues.getValue(); + this.props.onChange(this.props.selectedValuesList.filter(value => !selectedValues.find(selectedValue => selectedValue === value))); + } + + addAllToSelectedList() { + this.props.onChange(this.props.availableList.map(item => item.id)); + } + + removeAllFromSelectedList() { + this.props.onChange([]); + } +} + +export default DualListboxView; diff --git a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx new file mode 100644 index 0000000000..5daaffea41 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx @@ -0,0 +1,221 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classNames from 'classnames'; +import Select from 'nfvo-components/input/SelectInput.jsx'; + +export const other = {OTHER: 'Other'}; + +class InputOptions extends React.Component { + + static propTypes = { + values: React.PropTypes.arrayOf(React.PropTypes.shape({ + enum: React.PropTypes.string, + title: React.PropTypes.string + })), + isEnabledOther: React.PropTypes.bool, + title: React.PropTypes.string, + selectedValue: React.PropTypes.string, + multiSelectedEnum: React.PropTypes.array, + selectedEnum: React.PropTypes.string, + otherValue: React.PropTypes.string, + onEnumChange: React.PropTypes.func, + onOtherChange: React.PropTypes.func, + isRequired: React.PropTypes.bool, + isMultiSelect: React.PropTypes.bool + }; + + + static contextTypes = { + isReadOnlyMode: React.PropTypes.bool + }; + + state = { + otherInputDisabled: !this.props.otherValue + }; + + oldProps = { + selectedEnum: '', + otherValue: '', + multiSelectedEnum: [] + }; + + render() { + let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, hasError, validations, children} = this.props; + + let currentMultiSelectedEnum = []; + let currentSelectedEnum = ''; + let {otherInputDisabled} = this.state; + if (isMultiSelect) { + currentMultiSelectedEnum = multiSelectedEnum; + if(!otherInputDisabled) { + currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined; + } + } + else if(selectedEnum){ + currentSelectedEnum = selectedEnum; + } + + let isReadOnlyMode = this.context.isReadOnlyMode; + + return( +
      + {label && } + {isMultiSelect && otherInputDisabled ? + onBlur()} + disabled={isReadOnlyMode || Boolean(this.props.disabled)} + onChange={ value => this.enumChanged(value)} + type='select'> + {values && values.length && values.map(val => this.renderOptions(val))} + {onOtherChange && } + {children} + + + {!otherInputDisabled &&
      } + onBlur()} + onChange={() => this.changedOtherInput()}/> +
      + } +
      + ); + } + + renderOptions(val){ + return( + + ); + } + + + renderMultiSelectOptions(values) { + let {onOtherChange} = this.props; + let optionsList = []; + if (onOtherChange) { + optionsList = values.map(option => { + return { + label: option.title, + value: option.enum, + }; + }).concat([{ + label: i18n(other.OTHER), + value: i18n(other.OTHER), + }]); + } + else { + optionsList = values.map(option => { + return { + label: option.title, + value: option.enum, + }; + }); + } + if (optionsList.length > 0 && optionsList[0].value === '') { + optionsList.shift(); + } + return optionsList; + } + + getValue() { + let res = ''; + let {isMultiSelect} = this.props; + let {otherInputDisabled} = this.state; + + if (otherInputDisabled) { + res = isMultiSelect ? this.refs._myInput.getValue() : this.refs._myInput.value; + } else { + res = this.refs._otherValue.value; + } + return res; + } + + enumChanged() { + let enumValue = this.refs._myInput.value; + let {onEnumChange, isMultiSelect, onChange} = this.props; + this.setState({ + otherInputDisabled: enumValue !== other.OTHER + }); + + let value = isMultiSelect ? [enumValue] : enumValue; + if (onEnumChange) { + onEnumChange(value); + } + if (onChange) { + onChange(value); + } + } + + multiSelectEnumChanged(enumValue) { + let {onEnumChange} = this.props; + let selectedValues = enumValue.map(enumVal => { + return enumVal.value; + }); + + if (this.state.otherInputDisabled === false) { + selectedValues.shift(); + } + else if (selectedValues.includes(i18n(other.OTHER))) { + selectedValues = [i18n(other.OTHER)]; + } + + this.setState({ + otherInputDisabled: !selectedValues.includes(i18n(other.OTHER)) + }); + onEnumChange(selectedValues); + } + + changedOtherInput() { + let {onOtherChange} = this.props; + onOtherChange(this.refs._otherValue.value); + } + + componentDidUpdate() { + let {otherValue, selectedEnum, onInputChange, multiSelectedEnum} = this.props; + if (this.oldProps.otherValue !== otherValue + || this.oldProps.selectedEnum !== selectedEnum + || this.oldProps.multiSelectedEnum !== multiSelectedEnum) { + this.oldProps = { + otherValue, + selectedEnum, + multiSelectedEnum + }; + onInputChange(); + } + } + + static getTitleByName(values, name) { + for (let key of Object.keys(values)) { + let option = values[key].find(option => option.enum === name); + if (option) { + return option.title; + } + } + return name; + } + +} + +export default InputOptions; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx new file mode 100644 index 0000000000..a87c8d6f40 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx @@ -0,0 +1,40 @@ +/** + * Holds the buttons for save/reset for forms. + * Used by the ValidationForm that changes the state of the buttons according to its own state. + * + * properties: + * labledButtons - whether or not to use labeled buttons or icons only + */ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Button from 'react-bootstrap/lib/Button.js'; +import FontAwesome from 'react-fontawesome'; + +class ValidationButtons extends React.Component { + + static propTypes = { + labledButtons: React.PropTypes.bool.isRequired, + isReadOnlyMode: React.PropTypes.bool + }; + + state = { + isValid: this.props.formValid + }; + + render() { + var submitBtn = this.props.labledButtons ? i18n('Save') : ; + var closeBtn = this.props.labledButtons ? i18n('Cancel') : ; + return ( +
      + {!this.props.isReadOnlyMode ? +
      + + +
      + : + } +
      + ); + } +} +export default ValidationButtons; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx new file mode 100644 index 0000000000..098ccf1fd4 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx @@ -0,0 +1,200 @@ +/** + * ValidationForm should be used in order to have a form that handles it's internal validation state. + * All ValidationInputs inside the form are checked for validity and the styling and submit buttons + * are updated accordingly. + * + * The properties that ahould be given to the form: + * labledButtons - whether or not use icons only as the form default buttons or use buttons with labels + * onSubmit - function for click on the submit button + * onReset - function for click on the reset button + */ +import React from 'react'; +import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; +import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; +import ValidationButtons from './ValidationButtons.jsx'; + +class ValidationForm extends React.Component { + + static childContextTypes = { + validationParent: React.PropTypes.any, + isReadOnlyMode: React.PropTypes.bool, + validationSchema: React.PropTypes.instanceOf(JSONSchema), + validationData: React.PropTypes.object + }; + + static defaultProps = { + hasButtons : true, + onSubmit : null, + onReset : null, + labledButtons: true, + onValidChange : null, + isValid: true + }; + + static propTypes = { + isValid : React.PropTypes.bool, + isReadOnlyMode : React.PropTypes.bool, + hasButtons : React.PropTypes.bool, + onSubmit : React.PropTypes.func, + onReset : React.PropTypes.func, + labledButtons: React.PropTypes.bool, + onValidChange : React.PropTypes.func, + onValidityChanged: React.PropTypes.func, + schema: React.PropTypes.object, + data: React.PropTypes.object + }; + + state = { + isValid: this.props.isValid + }; + + constructor(props) { + super(props); + this.validationComponents = []; + } + + componentWillMount() { + let {schema, data} = this.props; + if (schema) { + this.processSchema(schema, data); + } + } + + componentWillReceiveProps(nextProps) { + let {schema, data} = this.props; + let {schema: nextSchema, data: nextData} = nextProps; + + if (schema !== nextSchema || data !== nextData) { + if (!schema || !nextSchema) { + throw new Error('ValidationForm: dynamically adding/removing schema is not supported'); + } + + if (schema !== nextSchema) { + this.processSchema(nextSchema, nextData); + } else { + this.setState({data: nextData}); + } + } + } + + processSchema(rawSchema, rawData) { + let schema = new JSONSchema(); + schema.setSchema(rawSchema); + let data = schema.processData(rawData); + this.setState({ + schema, + data + }); + } + + render() { + // eslint-disable-next-line no-unused-vars + let {isValid, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, schema, data, children, ...formProps} = this.props; + return ( +
      this.handleFormSubmit(event)}> +
      {children}
      + {hasButtons && } + + ); + } + + handleFormSubmit(event) { + event.preventDefault(); + let isFormValid = true; + this.validationComponents.forEach(validationComponent => { + const isInputValid = validationComponent.validate().isValid; + isFormValid = isInputValid && isFormValid; + }); + if(isFormValid && this.props.onSubmit) { + return this.props.onSubmit(event); + } else if(!isFormValid) { + this.setState({isValid: false}); + } + } + + componentWillUpdate(nextProps, nextState) { + if(this.state.isValid !== nextState.isValid && this.props.onValidityChanged) { + this.props.onValidityChanged(nextState.isValid); + } + } + + componentDidUpdate(prevProps, prevState) { + // only handling this programatically if the validation of the form is done outside of the view + // (example with a form that is dependent on the state of other forms) + if (prevProps.isValid !== this.props.isValid) { + if (this.props.hasButtons) { + this.refs.buttons.setState({isValid: this.state.isValid}); + } + } else if(this.state.isValid !== prevState.isValid) { + if (this.props.hasButtons) { + this.refs.buttons.setState({isValid: this.state.isValid}); + } + // callback in case form is part of bigger picture in view + if (this.props.onValidChange) { + this.props.onValidChange(this.state.isValid); + } + } + } + + componentDidMount() { + if (this.props.hasButtons) { + this.refs.buttons.setState({isValid: this.state.isValid}); + } + } + + + getChildContext() { + return { + validationParent: this, + isReadOnlyMode: this.props.isReadOnlyMode, + validationSchema: this.state.schema, + validationData: this.state.data + }; + } + + + /*** + * Used by ValidationInput in order to let the (parent) form know + * the valid state. If there is a change in the state of the form, + * the buttons will be updated. + * + * @param validationComponent + * @param isValid + */ + childValidStateChanged(validationComponent, isValid) { + if (isValid !== this.state.isValid) { + let oldState = this.state.isValid; + let newState = isValid && this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent).every(otherValidationComponent => { + return otherValidationComponent.isValid(); + }); + + if (oldState !== newState) { + this.setState({isValid: newState}); + } + } + } + + register(validationComponent) { + if (this.state.schema) { + // TODO: register + } else { + this.validationComponents.push(validationComponent); + } + } + + unregister(validationComponent) { + this.childValidStateChanged(validationComponent, true); + this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); + } + + onValueChanged(pointer, value, isValid, error) { + this.props.onDataChanged({ + data: JSONPointer.setValue(this.props.data, pointer, value), + isValid, + error + }); + } +} + + +export default ValidationForm; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx new file mode 100644 index 0000000000..0f14307645 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx @@ -0,0 +1,509 @@ +/** + * Used for inputs on a validation form. + * All properties will be passed on to the input element. + * + * The following properties can be set for OOB validations and callbacks: + - required: Boolean: Should be set to true if the input must have a value + - numeric: Boolean : Should be set to true id the input should be an integer + - onChange : Function : Will be called to validate the value if the default validations are not sufficient, should return a boolean value + indicating whether the value is valid + - didUpdateCallback :Function: Will be called after the state has been updated and the component has rerendered. This can be used if + there are dependencies between inputs in a form. + * + * The following properties of the state can be set to determine + * the state of the input from outside components: + - isValid : Boolean - whether the value is valid + - value : value for the input field, + - disabled : Boolean, + - required : Boolean - whether the input value must be filled out. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Validator from 'validator'; +import FormGroup from 'react-bootstrap/lib/FormGroup.js'; +import Input from 'react-bootstrap/lib/Input.js'; +import Overlay from 'react-bootstrap/lib/Overlay.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import isEqual from 'lodash/isEqual.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; +import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; + + +import InputOptions from '../inputOptions/InputOptions.jsx'; + +const globalValidationFunctions = { + required: value => value !== '', + maxLength: (value, length) => Validator.isLength(value, {max: length}), + minLength: (value, length) => Validator.isLength(value, {min: length}), + pattern: (value, pattern) => Validator.matches(value, pattern), + numeric: value => { + if (value === '') { + // to allow empty value which is not zero + return true; + } + return Validator.isNumeric(value); + }, + maxValue: (value, maxValue) => value < maxValue, + minValue: (value, minValue) => value >= minValue, + alphanumeric: value => Validator.isAlphanumeric(value), + alphanumericWithSpaces: value => Validator.isAlphanumeric(value.replace(/ /g, '')), + validateName: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-/g, ''), 'en-US'), + validateVendorName: value => Validator.isAlphanumeric(value.replace(/[\x7F-\xFF]|\s/g, ''), 'en-US'), + freeEnglishText: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-|\,|\(|\)|\?/g, ''), 'en-US'), + email: value => Validator.isEmail(value), + ip: value => Validator.isIP(value), + url: value => Validator.isURL(value) +}; + +const globalValidationMessagingFunctions = { + required: () => i18n('Field is required'), + maxLength: (value, maxLength) => i18n('Field value has exceeded it\'s limit, {maxLength}. current length: {length}', { + length: value.length, + maxLength + }), + minLength: (value, minLength) => i18n('Field value should contain at least {minLength} characters.', {minLength}), + pattern: (value, pattern) => i18n('Field value should match the pattern: {pattern}.', {pattern}), + numeric: () => i18n('Field value should contain numbers only.'), + maxValue: (value, maxValue) => i18n('Field value should be less than: {maxValue}.', {maxValue}), + minValue: (value, minValue) => i18n('Field value should be at least: {minValue}.', {minValue}), + alphanumeric: () => i18n('Field value should contain letters or digits only.'), + alphanumericWithSpaces: () => i18n('Field value should contain letters, digits or spaces only.'), + validateName: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), + validateVendorName: ()=> i18n('Field value should contain English letters digits and spaces only.'), + freeEnglishText: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), + email: () => i18n('Field value should be a valid email address.'), + ip: () => i18n('Field value should be a valid ip address.'), + url: () => i18n('Field value should be a valid url address.'), + general: () => i18n('Field value is invalid.') +}; + +class ValidationInput extends React.Component { + + static contextTypes = { + validationParent: React.PropTypes.any, + isReadOnlyMode: React.PropTypes.bool, + validationSchema: React.PropTypes.instanceOf(JSONSchema), + validationData: React.PropTypes.object + }; + + static defaultProps = { + onChange: null, + disabled: null, + didUpdateCallback: null, + validations: {}, + value: '' + }; + + static propTypes = { + type: React.PropTypes.string.isRequired, + onChange: React.PropTypes.func, + disabled: React.PropTypes.bool, + didUpdateCallback: React.PropTypes.func, + validations: React.PropTypes.object, + isMultiSelect: React.PropTypes.bool, + onOtherChange: React.PropTypes.func, + pointer: React.PropTypes.string + }; + + + state = { + isValid: true, + style: null, + value: this.props.value, + error: {}, + previousErrorMessage: '', + wasInvalid: false, + validations: this.props.validations, + isMultiSelect: this.props.isMultiSelect + }; + + componentWillMount() { + if (this.context.validationSchema) { + let {validationSchema: schema, validationData: data} = this.context, + {pointer} = this.props; + + if (!schema.exists(pointer)) { + console.error(`Field doesn't exists in the schema ${pointer}`); + } + + let value = JSONPointer.getValue(data, pointer); + if (value === undefined) { + value = schema.getDefault(pointer); + if (value === undefined) { + value = ''; + } + } + this.setState({value}); + + let enums = schema.getEnum(pointer); + if (enums) { + let values = enums.map(value => ({enum: value, title: value, groupName: pointer})), + isMultiSelect = schema.isArray(pointer); + + if (!isMultiSelect && this.props.type !== 'radiogroup') { + values = [{enum: '', title: i18n('Select...')}, ...values]; + } + if (isMultiSelect && Array.isArray(value) && value.length === 0) { + value = ''; + } + + this.setState({ + isMultiSelect, + values, + onEnumChange: value => this.changedInputOptions(value), + value + }); + } + + this.setState({validations: this.extractValidationsFromSchema(schema, pointer, this.props)}); + } + } + + extractValidationsFromSchema(schema, pointer, props) { + /* props are here to get precedence over the scheme definitions */ + let validations = {}; + + if (schema.isRequired(pointer)) { + validations.required = true; + } + + if (schema.isNumber(pointer)) { + validations.numeric = true; + + const maxValue = props.validations.maxValue || schema.getMaxValue(pointer); + if (maxValue !== undefined) { + validations.maxValue = maxValue; + } + + const minValue = props.validations.minValue || schema.getMinValue(pointer); + if (minValue !== undefined) { + validations.minValue = minValue; + } + } + + + if (schema.isString(pointer)) { + + const pattern = schema.getPattern(pointer); + if (pattern) { + validations.pattern = pattern; + } + + const maxLength = schema.getMaxLength(pointer); + if (maxLength !== undefined) { + validations.maxLength = maxLength; + } + + const minLength = schema.getMinLength(pointer); + if (minLength !== undefined) { + validations.minLength = minLength; + } + } + + return validations; + } + + componentWillReceiveProps({value: nextValue, validations: nextValidations, pointer: nextPointer}, nextContext) { + const {validations, value} = this.props; + const validationsChanged = !isEqual(validations, nextValidations); + if (nextContext.validationSchema) { + if (this.props.pointer !== nextPointer || + this.context.validationData !== nextContext.validationData) { + let currentValue = JSONPointer.getValue(this.context.validationData, this.props.pointer), + nextValue = JSONPointer.getValue(nextContext.validationData, nextPointer); + if(nextValue === undefined) { + nextValue = ''; + } + if (this.state.isMultiSelect && Array.isArray(nextValue) && nextValue.length === 0) { + nextValue = ''; + } + if (currentValue !== nextValue) { + this.setState({value: nextValue}); + } + if (validationsChanged) { + this.setState({ + validations: this.extractValidationsFromSchema(nextContext.validationSchema, nextPointer, {validations: nextValidations}) + }); + } + } + } else { + if (validationsChanged) { + this.setState({validations: nextValidations}); + } + if (this.state.wasInvalid && (value !== nextValue || validationsChanged)) { + this.validate(nextValue, nextValidations); + } else if (value !== nextValue) { + this.setState({value: nextValue}); + } + } + } + + shouldTypeBeNumberBySchemeDefinition(pointer) { + return this.context.validationSchema && + this.context.validationSchema.isNumber(pointer); + } + + hasEnum(pointer) { + return this.context.validationSchema && + this.context.validationSchema.getEnum(pointer); + } + + render() { + let {value, isMultiSelect, values, onEnumChange, style, isValid, validations} = this.state; + let {onOtherChange, type, pointer} = this.props; + if (this.shouldTypeBeNumberBySchemeDefinition(pointer) && !this.hasEnum(pointer)) { + type = 'number'; + } + let props = {...this.props}; + + let groupClasses = this.props.groupClassName || ''; + if (validations.required) { + groupClasses += ' required'; + } + let isReadOnlyMode = this.context.isReadOnlyMode; + + if (value === true && (type === 'checkbox' || type === 'radio')) { + props.checked = true; + } + return ( +
      + { + !isMultiSelect && !onOtherChange && type !== 'select' && type !== 'radiogroup' + && this.changedInput()} + onBlur={() => this.blurInput()}> + {this.props.children} + + } + { + type === 'radiogroup' + && + { + values.map(val => + this.changedInput()}/> + ) + } + + } + { + (isMultiSelect || onOtherChange || type === 'select') + && this.changedInput()} + onBlur={() => this.blurInput()} + hasError={!isValid} + ref={'_myInput'} + isMultiSelect={isMultiSelect} + values={values} + onEnumChange={onEnumChange} + selectedEnum={value} + multiSelectedEnum={value} + {...props} /> + } + {this.renderOverlay()} +
      + ); + } + + renderOverlay() { + let position = 'right'; + if (this.props.type === 'text' + || this.props.type === 'email' + || this.props.type === 'number' + || this.props.type === 'password' + + ) { + position = 'bottom'; + } + + let validationMessage = this.state.error.message || this.state.previousErrorMessage; + return ( + { + let target = ReactDOM.findDOMNode(this.refs._myInput); + return target.offsetParent ? target : undefined; + }} + container={this}> + + {validationMessage} + + + ); + } + + componentDidMount() { + if (this.context.validationParent) { + this.context.validationParent.register(this); + } + } + + componentDidUpdate(prevProps, prevState) { + if (this.context.validationParent) { + if (prevState.isValid !== this.state.isValid) { + this.context.validationParent.childValidStateChanged(this, this.state.isValid); + } + } + if (this.props.didUpdateCallback) { + this.props.didUpdateCallback(); + } + + } + + componentWillUnmount() { + if (this.context.validationParent) { + this.context.validationParent.unregister(this); + } + } + + isNumberInputElement() { + return this.props.type === 'number' || this.refs._myInput.props.type === 'number'; + } + + /*** + * Adding same method as the actual input component + * @returns {*} + */ + getValue() { + if (this.props.type === 'checkbox') { + return this.refs._myInput.getChecked(); + } + if (this.props.type === 'radiogroup') { + for (let key in this.refs) { // finding the value of the radio button that was checked + if (this.refs[key].getChecked()) { + return this.refs[key].getValue(); + } + } + } + if (this.isNumberInputElement()) { + return Number(this.refs._myInput.getValue()); + } + + return this.refs._myInput.getValue(); + } + + resetValue() { + this.setState({value: this.props.value}); + } + + + /*** + * internal method that validated the value. includes callback to the onChange method + * @param value + * @param validations - map containing validation id and the limitation describing the validation. + * @returns {object} + */ + validateValue = (value, validations) => { + let {customValidationFunction} = validations; + let error = {}; + let isValid = true; + for (let validation in validations) { + if ('customValidationFunction' !== validation) { + if (validations[validation]) { + if (!globalValidationFunctions[validation](value, validations[validation])) { + error.id = validation; + error.message = globalValidationMessagingFunctions[validation](value, validations[validation]); + isValid = false; + break; + } + } + } else { + let customValidationResult = customValidationFunction(value); + + if (customValidationResult !== true) { + error.id = 'custom'; + isValid = false; + if (typeof customValidationResult === 'string') {//custom validation error message supplied. + error.message = customValidationResult; + } else { + error.message = globalValidationMessagingFunctions.general(); + } + break; + } + + + } + } + + return { + isValid, + error + }; + }; + + /*** + * Internal method that handles the change event of the input. validates and updates the state. + */ + changedInput() { + + let {isValid, error} = this.state.wasInvalid ? this.validate() : this.state; + let onChange = this.props.onChange; + if (onChange) { + onChange(this.getValue(), isValid, error); + } + if (this.context.validationSchema) { + let value = this.getValue(); + if (this.state.isMultiSelect && value === '') { + value = []; + } + if (this.shouldTypeBeNumberBySchemeDefinition(this.props.pointer)) { + value = Number(value); + } + this.context.validationParent.onValueChanged(this.props.pointer, value, isValid, error); + } + } + + changedInputOptions(value) { + this.context.validationParent.onValueChanged(this.props.pointer, value, true); + } + + blurInput() { + if (!this.state.wasInvalid) { + this.setState({wasInvalid: true}); + } + + let {isValid, error} = !this.state.wasInvalid ? this.validate() : this.state; + let onBlur = this.props.onBlur; + if (onBlur) { + onBlur(this.getValue(), isValid, error); + } + } + + validate(value = this.getValue(), validations = this.state.validations) { + let validationStatus = this.validateValue(value, validations); + let {isValid, error} = validationStatus; + let _style = isValid ? null : 'error'; + this.setState({ + isValid, + error, + value, + previousErrorMessage: this.state.error.message || '', + style: _style, + wasInvalid: !isValid || this.state.wasInvalid + }); + + return validationStatus; + } + + isValid() { + return this.state.isValid; + } + +} +export default ValidationInput; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx new file mode 100644 index 0000000000..6036518288 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx @@ -0,0 +1,107 @@ +import React from 'react'; +import Tab from 'react-bootstrap/lib/Tab.js'; + +export default +class ValidationTab extends React.Component { + + static propTypes = { + children: React.PropTypes.node, + eventKey: React.PropTypes.any.isRequired, + onValidationStateChange: React.PropTypes.func //This property is assigned dynamically via React.cloneElement. lookup ValidationTabs.jsx. therefore it cannot be stated as required! + }; + + constructor(props) { + super(props); + this.validationComponents = []; + } + + static childContextTypes = { + validationParent: React.PropTypes.any + }; + + static contextTypes = { + validationParent: React.PropTypes.any + }; + + getChildContext() { + return {validationParent: this}; + } + + state = { + isValid: true, + notifyParent: false + }; + + componentDidMount() { + let validationParent = this.context.validationParent; + if (validationParent) { + validationParent.register(this); + } + } + + componentWillUnmount() { + let validationParent = this.context.validationParent; + if (validationParent) { + validationParent.unregister(this); + } + } + + register(validationComponent) { + this.validationComponents.push(validationComponent); + } + + unregister(validationComponent) { + this.childValidStateChanged(validationComponent, true); + this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); + } + + notifyValidStateChangedToParent(isValid) { + + let validationParent = this.context.validationParent; + if (validationParent) { + validationParent.childValidStateChanged(this, isValid); + } + } + + childValidStateChanged(validationComponent, isValid) { + + const currentValidState = this.state.isValid; + if (isValid !== currentValidState) { + let filteredValidationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); + let newValidState = isValid && filteredValidationComponents.every(otherValidationComponent => { + return otherValidationComponent.isValid(); + }); + this.setState({isValid: newValidState, notifyParent: true}); + } + } + + validate() { + let isValid = true; + this.validationComponents.forEach(validationComponent => { + const isValidationComponentValid = validationComponent.validate().isValid; + isValid = isValidationComponentValid && isValid; + }); + this.setState({isValid, notifyParent: false}); + return {isValid}; + } + + componentDidUpdate(prevProps, prevState) { + if(prevState.isValid !== this.state.isValid) { + if(this.state.notifyParent) { + this.notifyValidStateChangedToParent(this.state.isValid); + } + this.props.onValidationStateChange(this.props.eventKey, this.state.isValid); + } + } + + isValid() { + return this.state.isValid; + } + + render() { + let {children, ...tabProps} = this.props; + return ( + {children} + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx new file mode 100644 index 0000000000..6eda4b9827 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx @@ -0,0 +1,72 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Tabs from 'react-bootstrap/lib/Tabs.js'; +import Overlay from 'react-bootstrap/lib/Overlay.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export default +class ValidationTab extends React.Component { + + static propTypes = { + children: React.PropTypes.node + }; + + state = { + invalidTabs: [] + }; + + cloneTab(element) { + const {invalidTabs} = this.state; + return React.cloneElement( + element, + { + key: element.props.eventKey, + tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab', + onValidationStateChange: (eventKey, isValid) => this.validTabStateChanged(eventKey, isValid) + } + ); + } + + validTabStateChanged(eventKey, isValid) { + let {invalidTabs} = this.state; + let invalidTabIndex = invalidTabs.indexOf(eventKey); + if (isValid && invalidTabIndex > -1) { + this.setState({invalidTabs: invalidTabs.filter(otherEventKey => eventKey !== otherEventKey)}); + } else if (!isValid && invalidTabIndex === -1) { + this.setState({invalidTabs: [...invalidTabs, eventKey]}); + } + } + + showTabsError() { + const {invalidTabs} = this.state; + return invalidTabs.length > 0 && (invalidTabs.length > 1 || invalidTabs[0] !== this.props.activeKey); + } + + render() { + return ( +
      + + {this.props.children.map(element => this.cloneTab(element))} + + { + let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); + return target && target.offsetParent ? target : undefined; + } + } + container={this}> + + {i18n('One or more tabs are invalid')} + + +
      + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx new file mode 100644 index 0000000000..e8d0fc2536 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import store from 'sdc-app/AppStore.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; + +class ListEditorItem extends React.Component { + static propTypes = { + onSelect: React.PropTypes.func, + onDelete: React.PropTypes.func, + onEdit: React.PropTypes.func, + children: React.PropTypes.node, + isReadOnlyMode: React.PropTypes.bool + } + + render() { + let {onDelete, onSelect, onEdit, children, isReadOnlyMode} = this.props; + let isAbilityToDelete = isReadOnlyMode === undefined ? true : !isReadOnlyMode; + return ( +
      +
      + {children} +
      +
      + {onEdit && this.onClickedItem(onEdit)}/>} + {onDelete && isAbilityToDelete && this.onClickedItem(onDelete)}/>} +
      +
      + ); + } + + onClickedItem(callBackFunc) { + if(typeof callBackFunc === 'function') { + let {isCheckedOut} = this.props; + if (isCheckedOut === false) { + store.dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {title: 'Error', msg: 'This item is checkedin/submitted, Click Check Out to continue'} + }); + } + else { + callBackFunc(); + } + } + } +} + +export default ListEditorItem; diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx new file mode 100644 index 0000000000..1ee91f31f6 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx @@ -0,0 +1,64 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import Input from 'react-bootstrap/lib/Input.js'; + + +class ListEditorView extends React.Component { + + static defaultProps = { + className: '' + }; + + static propTypes = { + title: React.PropTypes.string, + plusButtonTitle: React.PropTypes.string, + children: React.PropTypes.node, + filterValue: React.PropTypes.string, + onFilter: React.PropTypes.func, + className: React.PropTypes.string, + isReadOnlyMode: React.PropTypes.bool, + placeholder: React.PropTypes.string + }; + + render() { + let {title, plusButtonTitle, onAdd, children, filterValue, onFilter, className, placeholder, isReadOnlyMode} = this.props; + return ( +
      + {title && onAdd &&
      {title}
      } +
      + {title && !onAdd &&
      {title}
      } +
      + { onAdd && +
      + + {plusButtonTitle} +
      + } +
      + + { + onFilter && +
      + onFilter(this.refs.filter.getValue())}/> + +
      + } +
      +
      +
      + {children} +
      +
      +
      + ); + } + +} +export default ListEditorView; diff --git a/openecomp-ui/src/nfvo-components/loader/Loader.jsx b/openecomp-ui/src/nfvo-components/loader/Loader.jsx new file mode 100644 index 0000000000..cc1ffdb2b3 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/loader/Loader.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import {connect} from 'react-redux'; + +const mapStateToProps = ({loader}) => { + return { + isLoading: loader.isLoading + }; +}; + +class Loader extends React.Component { + + static propTypes = { + isLoading: React.PropTypes.bool.isRequired + }; + + static defaultProps = { + isLoading: false + }; + + render() { + let {isLoading} = this.props; + + return ( +
      + { + isLoading &&
      +
      +
      + } +
      + ); + } +} + +export default connect(mapStateToProps) (Loader); diff --git a/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js b/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js new file mode 100644 index 0000000000..e8e4953eb9 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js @@ -0,0 +1,26 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + SHOW: null, + HIDE: null +}); diff --git a/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js b/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js new file mode 100644 index 0000000000..582eff330d --- /dev/null +++ b/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js @@ -0,0 +1,32 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './LoaderConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SHOW: + return {isLoading: true}; + case actionTypes.HIDE: + return {isLoading: false}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/nfvo-components/modal/Modal.jsx b/openecomp-ui/src/nfvo-components/modal/Modal.jsx new file mode 100644 index 0000000000..be4963ef65 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/Modal.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import BootstrapModal from 'react-bootstrap/lib/Modal.js'; + +let nextModalId = 0; + +export default class Modal extends React.Component { + + static Header = BootstrapModal.Header; + + static Title = BootstrapModal.Title; + + static Footer = BootstrapModal.Footer; + + static Body = class ModalBody extends React.Component { + + render() { + let {children, ...props} = this.props; + return ( + + {children} + + ); + } + + componentDidMount() { + let element = ReactDOM.findDOMNode(this); + element.addEventListener('click', event => { + if (event.target.tagName === 'A') { + event.preventDefault(); + } + }); + ['wheel', 'mousewheel', 'DOMMouseScroll'].forEach(eventType => + element.addEventListener(eventType, event => event.stopPropagation()) + ); + } + }; + + componentWillMount() { + this.modalId = `dox-ui-modal-${nextModalId++}`; + } + + componentDidMount() { + this.ensureRootClass(); + } + + componentDidUpdate() { + this.ensureRootClass(); + } + + ensureRootClass() { + let element = document.getElementById(this.modalId); + while(element && !element.hasAttribute('data-reactroot')) { + element = element.parentElement; + } + if (element && !element.classList.contains('dox-ui')) { + element.classList.add('dox-ui'); + } + } + + render() { + let {children, ...props} = this.props; + return ( + + {children} + + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js b/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js new file mode 100644 index 0000000000..1a53f4c135 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js @@ -0,0 +1,29 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export default keyMirror({ + NOTIFY_ERROR: null, + NOTIFY_SUCCESS: null, + NOTIFY_WARNING: null, + NOTIFY_INFO: null, + NOTIFY_CLOSE: null +}); diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx b/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx new file mode 100644 index 0000000000..71793097fb --- /dev/null +++ b/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx @@ -0,0 +1,100 @@ +/** + * NotificationModal options: + * + * show: whether to show notification or not, + * type: the type of the notification. valid values are: 'default', 'error', 'warning', 'success' + * msg: the notification content. could be a string or node (React component) + * title: the notification title + * timeout: timeout for the notification to fade out. if timeout == 0 then the notification is rendered until the user closes it + * + */ +import React, {Component, PropTypes} from 'react'; +import {connect} from 'react-redux'; +import Button from 'react-bootstrap/lib/Button.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx'; +import NotificationConstants from './NotificationConstants.js'; + +let typeClass = { + 'default': 'primary', + error: 'danger', + warning: 'warning', + success: 'success' +}; + +const mapActionsToProps = (dispatch) => { + return {onCloseClick: () => dispatch({type: NotificationConstants.NOTIFY_CLOSE})}; +}; + +const mapStateToProps = ({notification}) => { + + let show = notification !== null && notification.title !== 'Conflict'; + let mapResult = {show}; + if (show) { + mapResult = {show, ...notification}; + } + + return mapResult; +}; + +export class NotificationModal extends Component { + + static propTypes = { + show: PropTypes.bool, + type: PropTypes.oneOf(['default', 'error', 'warning', 'success']), + title: PropTypes.string, + msg: PropTypes.node, + validationResponse: PropTypes.object, + timeout: PropTypes.number + }; + + static defaultProps = { + show: false, + type: 'default', + title: '', + msg: '', + timeout: 0 + }; + + state = {type: undefined}; + + componentWillReceiveProps(nextProps) { + if (this.props.show !== nextProps.show && nextProps.show === false) { + this.setState({type: this.props.type}); + } + else { + this.setState({type: undefined}); + } + } + + componentDidUpdate() { + if (this.props.timeout) { + setTimeout(this.props.onCloseClick, this.props.timeout); + } + } + + render() { + let {title, type, msg, show, validationResponse, onCloseClick} = this.props; + if (!show) { + type = this.state.type; + } + if (validationResponse) { + msg = (); + } + return ( + + + {title} + + {msg} + + + + + ); + } +} + +export default connect(mapStateToProps, mapActionsToProps)(NotificationModal); diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js b/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js new file mode 100644 index 0000000000..c8b30d6e50 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js @@ -0,0 +1,51 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import NotificationConstants from './NotificationConstants.js'; + +export default (state = null, action) => { + switch (action.type) { + case NotificationConstants.NOTIFY_INFO: + return {type: 'default', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout}; + + case NotificationConstants.NOTIFY_ERROR: + return { + type: 'error', + title: action.data.title, + msg: action.data.msg, + validationResponse: action.data.validationResponse, + timeout: action.data.timeout + }; + + case NotificationConstants.NOTIFY_WARNING: + return {type: 'warning', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout}; + + case NotificationConstants.NOTIFY_SUCCESS: + return { + type: 'success', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout + }; + case NotificationConstants.NOTIFY_CLOSE: + return null; + + default: + return state; + } + +}; diff --git a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx new file mode 100644 index 0000000000..feb0f813ea --- /dev/null +++ b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx @@ -0,0 +1,73 @@ +import React from 'react'; +import classnames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; + +class NavigationSideBar extends React.Component { + + static PropTypes = { + activeItemId: React.PropTypes.string.isRequired, + onSelect: React.PropTypes.func, + onToggle: React.PropTypes.func, + groups: React.PropTypes.array + }; + + render() { + let {groups, activeItemId} = this.props; + + return ( +
      + {groups.map(group => ( +
      +
      {group.name}
      +
      + { + group.items && group.items.map(item => this.renderGroupItem(item, activeItemId)) + } +
      +
      + ))} +
      + ); + } + + renderGroupItem(item, activeItemId) { + let isGroup = item.items && item.items.length > 0; + return ( +
      +
      this.handleItemClicked(event, item)}> + {item.name} +
      + {isGroup && + +
      + {item.items.map(item => this.renderGroupItem(item, activeItemId))} +
      +
      + } +
      + ); + } + + handleItemClicked(event, item) { + event.stopPropagation(); + if(this.props.onToggle) { + this.props.onToggle(this.props.groups, item.id); + } + if(item.onSelect) { + item.onSelect(); + } + if(this.props.onSelect) { + this.props.onSelect(item); + } + } +} + +export default NavigationSideBar; diff --git a/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx b/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx new file mode 100644 index 0000000000..10c5326300 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx @@ -0,0 +1,109 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import ReactDOM from 'react-dom'; + +class SlidePanel extends React.Component { + + static PropTypes = { + direction: React.PropTypes.string.isRequired, + className: React.PropTypes.string, + title: React.PropTypes.string, + isOpen: React.PropTypes.bool + }; + + static defaultProps = { + title: '', + className: '', + isOpen: true + }; + + state = { + isOpen: this.props.isOpen, + direction: this.props.direction, + width: 0, + arrowWidth: 0 + }; + + componentDidMount() { + this.setSliderPosition(); + } + + componentDidUpdate() { + this.setSliderPosition(); + } + + render() { + + let {children, className} = this.props; + let {isOpen} = this.state; + + return ( +
      + {this.renderHeader(isOpen)} +
      {children}
      +
      + ); + } + + renderHeader(isOpen) { + let {direction: initialDirection, title} = this.props; + let {direction: currentDirection} = this.state; + + let iconName = currentDirection === 'right' ? 'angle-double-right collapse-double-icon' : 'angle-double-left collapse-double-icon'; + + let awestyle = {padding: '5px'}; + + if (!isOpen && initialDirection === 'right') { + awestyle.marginLeft = '-1px'; + } + return ( +
      + { initialDirection === 'left' && {title}} + + { initialDirection === 'right' && {title}} +
      + ); + } + + handleClick = () => { + this.setState({ + isOpen: !this.state.isOpen, + direction: this.state.direction === 'left' ? 'right' : 'left' + }); + } + + setSliderPosition = () => { + + let el = ReactDOM.findDOMNode(this); + let {style} = el; + + let {direction: initialDirection} = this.props; + let arrowIconSize = Math.floor(ReactDOM.findDOMNode(this.refs.arrowIcon).getBoundingClientRect().width) * 2; + if (!this.state.isOpen) { + if (this.props.direction === 'left') { + style.left = arrowIconSize - el.getBoundingClientRect().width + 'px'; + } + if (initialDirection === 'right') { + style.right = arrowIconSize - el.getBoundingClientRect().width + 'px'; + } + } + else { + if (initialDirection === 'left') { + style.left = '0px'; + } + + if (this.props.direction === 'right') { + style.right = '0px'; + } + } + } + +} + +export default SlidePanel; \ No newline at end of file diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx new file mode 100644 index 0000000000..78525f84c6 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx @@ -0,0 +1,165 @@ +import React from 'react'; +import classnames from 'classnames'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import Navbar from 'react-bootstrap/lib/Navbar.js'; +import Nav from 'react-bootstrap/lib/Nav.js'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import {actionsEnum, statusEnum} from './VersionControllerConstants.js'; + + +class VersionController extends React.Component { + + static propTypes = { + version: React.PropTypes.string, + viewableVersions: React.PropTypes.array, + onVersionSwitching: React.PropTypes.func, + isCheckedOut: React.PropTypes.bool.isRequired, + status: React.PropTypes.string.isRequired, + callVCAction: React.PropTypes.func, + onSave: React.PropTypes.func, + onClose: React.PropTypes.func, + isFormDataValid: React.PropTypes.bool + }; + + render() { + let {status, isCheckedOut, version = '', viewableVersions = [], onVersionSwitching, callVCAction, onSave, isFormDataValid, onClose} = this.props; + let isCheckedIn = Boolean(status === statusEnum.CHECK_IN_STATUS); + let isLatestVersion = Boolean(version === viewableVersions[viewableVersions.length - 1]); + if (!isLatestVersion) { + status = statusEnum.PREVIOUS_VERSION; + } + + return ( +
      + + + + + + +
      + ); + } + + renderStatus(status) { + switch (status) { + case statusEnum.CHECK_OUT_STATUS: + return ( +
      +
      +
      {i18n('CHECKED OUT')}
      +
      + ); + case statusEnum.LOCK_STATUS: + return ( +
      {i18n('LOCKED')}
      + ); + case statusEnum.CHECK_IN_STATUS: + return ( +
      {i18n('CHECKED IN')}
      + ); + case statusEnum.SUBMIT_STATUS: + return ( +
      {i18n('SUBMITTED')}
      + ); + default: + return ( +
      {i18n(status)}
      + ); + } + } + + checkinCheckoutVersion(callVCAction) { + if (this.props.isCheckedOut) { + this.checkin(callVCAction); + } + else { + this.checkout(callVCAction); + } + } + + checkin(callVCAction) { + + const action = actionsEnum.CHECK_IN; + + if (this.props.onSave) { + this.props.onSave().then(()=>{ + callVCAction(action); + }); + }else{ + callVCAction(action); + } + + } + + checkout(callVCAction) { + const action = actionsEnum.CHECK_OUT; + callVCAction(action); + } + + submit(callVCAction) { + const action = actionsEnum.SUBMIT; + callVCAction(action); + } + + revertCheckout(callVCAction) { + const action = actionsEnum.UNDO_CHECK_OUT; + callVCAction(action); + } +} + +export default VersionController; diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js new file mode 100644 index 0000000000..9251fd12c4 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionsEnum = keyMirror({ + CHECK_IN: 'Checkin', + CHECK_OUT: 'Checkout', + UNDO_CHECK_OUT: 'Undo_Checkout', + SUBMIT: 'Submit', + CREATE_PACKAGE: 'Create_Package' +}); + +export const statusEnum = keyMirror({ + CHECK_OUT_STATUS: 'Locked', + CHECK_IN_STATUS: 'Available', + SUBMIT_STATUS: 'Final', + LOCK_STATUS: 'LockedByUser', + PREVIOUS_VERSION: 'READ ONLY' +}); + diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js new file mode 100644 index 0000000000..de9914454c --- /dev/null +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import Configuration from 'sdc-app/config/Configuration.js'; +import {statusEnum} from './VersionControllerConstants.js'; + + +const VersionControllerUtils = { + + getCheckOutStatusKindByUserID(status, lockingUser) { + let currentLoginUserID = Configuration.get('ATTUserID'); + let isCheckedOut = currentLoginUserID === lockingUser; + + return { + status: isCheckedOut ? status : statusEnum.LOCK_STATUS, + isCheckedOut + }; + }, + + isCheckedOutByCurrentUser(resource) { + let currentLoginUserID = Configuration.get('ATTUserID'); + return resource.lockingUser !== undefined && resource.lockingUser === currentLoginUserID; + }, + + isReadOnly(resource) { + const {version, viewableVersions = []} = resource; + const latestVersion = viewableVersions[viewableVersions.length - 1]; + return version !== latestVersion || !VersionControllerUtils.isCheckedOutByCurrentUser(resource); + } +}; + +export default VersionControllerUtils; diff --git a/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx b/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx new file mode 100644 index 0000000000..d786aeef8b --- /dev/null +++ b/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx @@ -0,0 +1,22 @@ +import React from 'react'; + +class ProgressBar extends React.Component { + static propTypes = { + label: React.PropTypes.string, + now: React.PropTypes.string.isRequired + } + render() { + let {label, now} = this.props; + + return( +
      +
      +
      +
      +
      {label}
      +
      + ); + } +} + +export default ProgressBar; diff --git a/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js b/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js new file mode 100644 index 0000000000..0d27204bef --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/ErrorResponseHandler.js @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import store from 'sdc-app/AppStore.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; + +function showVariablesInMessage(variables, msg) { + let regex; + variables.forEach((value, index) => { + value = value.replace(';', ','); + regex = new RegExp('\'\%' + (index + 1) + '\''); + msg = msg.replace(regex, value); + }); + return msg; +} + +function parseATTExceptionObject(responseJSON) { + let title, msg; + if (responseJSON.requestError && responseJSON.requestError.policyException) { + title = 'Error: ' + responseJSON.requestError.policyException.messageId; + msg = responseJSON.requestError.policyException.text; + } + else if (responseJSON.requestError && responseJSON.requestError.serviceException) { + title = 'Error: ' + responseJSON.requestError.serviceException.messageId; + msg = responseJSON.requestError.serviceException.text; + let {variables} = responseJSON.requestError.serviceException; + if (variables) { + msg = showVariablesInMessage(variables, msg); + } + } + else { + title = responseJSON.status; + msg = responseJSON.message; + } + return {title, msg}; +} + +var errorResponseHandler = (xhr/*, textStatus, errorThrown*/) => { + let errorData; + if (xhr.responseJSON) { + errorData = parseATTExceptionObject(xhr.responseJSON); + } + else { + errorData = { + title: xhr.statusText, + msg: xhr.responseText + }; + } + store.dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {...errorData} + }); +}; + +export default errorResponseHandler; diff --git a/openecomp-ui/src/nfvo-utils/KeyMirror.js b/openecomp-ui/src/nfvo-utils/KeyMirror.js new file mode 100644 index 0000000000..eb50d31e07 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/KeyMirror.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var keyMirror = function (obj) { + var ret = {}; + var key; + var val; + if (!(obj instanceof Object && !Array.isArray(obj))) { + throw new Error('keyMirror(...): Argument must be an object.'); + } + for (key in obj) { + if (obj.hasOwnProperty(key)) { + val = obj[key]; + if (val instanceof Object) { + ret[key] = keyMirror(obj[key]); + } else if(val !== null && val !== undefined){ + ret[key] = val; + } + else { + ret[key] = Symbol(key); + } + } + } + return Object.freeze(ret); +}; + +export default keyMirror; diff --git a/openecomp-ui/src/nfvo-utils/RestAPIUtil.js b/openecomp-ui/src/nfvo-utils/RestAPIUtil.js new file mode 100644 index 0000000000..24734739a2 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/RestAPIUtil.js @@ -0,0 +1,288 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import _extend from 'lodash/extend.js'; +import _clone from 'lodash/clone.js'; +import _defaults from 'lodash/defaults.js'; +import $ from 'jquery'; +import uuid from 'uuid-js'; +import md5 from 'md5'; + +import store from 'sdc-app/AppStore.js'; +import {actionTypes as LoaderConstants} from 'nfvo-components/loader/LoaderConstants.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import errorResponseHandler from './ErrorResponseHandler.js'; + +const methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'delete': 'DELETE', + 'read': 'GET' +}; +const AUTHORIZATION_HEADER = 'X-AUTH-TOKEN'; +const STORAGE_AUTH_KEY = 'sdc-auth-token'; +const REQUEST_ID_HEADER = 'X-ECOMP-RequestID'; +const CONTENT_MD5_HEADER = 'Content-MD5'; +const namedParam = /{(\w+)}/g; +const queryParamsNames = { + pageStart: 'pageStart', + pageSize: 'pageSize', + sortField: 'sortField', + sortDir: 'sortDir', + filtering: 'filter' +}; + + +// jQuery binary transport to download files through XHR +// http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ +// https://github.com/henrya/js-jquery/tree/master/BinaryTransport +$.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) { + // check for conditions and support for blob / arraybuffer response type + if (window.FormData && ((options.dataType && (options.dataType === 'binary')) || + (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || + (window.Blob && options.data instanceof Blob)))) + ) { + return { + // create new XMLHttpRequest + send: function (headers, callback) { + // setup all variables + var xhr = new XMLHttpRequest(), + url = options.url, + type = options.type, + async = options.async || true, + // blob or arraybuffer. Default is blob + dataType = options.responseType || 'blob', + data = options.data || null, + username = options.username || null, + password = options.password || null; + + xhr.addEventListener('load', function () { + var data = {}; + data[options.dataType] = xhr.response; + // make callback and send data + callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); + }); + + xhr.open(type, url, async, username, password); + + // setup custom headers + for (var i in headers) { + xhr.setRequestHeader(i, headers[i]); + } + + xhr.responseType = dataType; + xhr.send(data); + }, + abort: function () { + } + }; + } +}); + +$(document).ajaxStart(()=> store.dispatch({type: LoaderConstants.SHOW})); +$(document).ajaxStop(()=> store.dispatch({type: LoaderConstants.HIDE})); + +function urlError() { + throw new Error('A "url" property or function must be specified'); +}; + +export function makeQueryParams(options) { + var qParams = {}; + if (options.pagination) { + qParams[queryParamsNames.pageStart] = options.pagination.pageStart; + qParams[queryParamsNames.pageSize] = options.pagination.pageSize; + } + if (options.sorting) { + qParams[queryParamsNames.sortField] = options.sorting.sortField; + qParams[queryParamsNames.sortDir] = options.sorting.sortDir; + } + if (options.filtering) { + qParams[queryParamsNames.filtering] = JSON.stringify(options.filtering); + } + + return _defaults(qParams, options.qParams); +} + +function appendQueryParam(p, value) { + var str = ''; + + if (value instanceof Array) { + if (value.length === 1) { + str = appendQueryParam(p, value[0]); + } else if (value.length > 1) { + str = appendQueryParam(p, value.shift()) + '&' + appendQueryParam(p, value); + } + } else { + str = p + '=' + encodeURIComponent(value); + } + + return str; +} + +function appendQueryString(url, qParams) { + var str = ''; + for (var param in qParams) { + str += (str ? '&' : '') + appendQueryParam(param, qParams[param]); + } + return url + (str ? '?' : '') + str; +} + +function composeURL(baseUrl, options) { + var url = baseUrl || urlError(); + if (options.url) { + delete options.url; + } + + var qParams = makeQueryParams(options); + url = appendQueryString(url, qParams); + + var matches = url.match(namedParam); + if (matches) { + for (var i = 0; i < matches.length; i++) { + var param = matches[i].substring(1, matches[i].length - 1); + var value = (options.params && options.params[param]); + + if (value === undefined) { + value = options[param]; + } + url = url.replace(matches[i], encodeURIComponent(value)); + } + } + + return url; +} + +function applyMD5Header(options, data) { + if (options.md5) { + let headers = options.headers; + headers[CONTENT_MD5_HEADER] = window.btoa(md5(JSON.stringify(data)).toLowerCase()); + } +} + +function applySecurity(options, data) { + var headers = options.headers || (options.headers = {}); + + var authToken = localStorage.getItem(STORAGE_AUTH_KEY); + if (authToken) { + headers[AUTHORIZATION_HEADER] = authToken; + } + + var attApiHeaders = Configuration.get('ATTApiHeaders'), + attUidHeader = attApiHeaders && attApiHeaders.userId; + if (attUidHeader) { + headers[attUidHeader.name] = attUidHeader.value; + } + + headers[REQUEST_ID_HEADER] = uuid.create().toString(); + + applyMD5Header(options, data); +} + +function handleResponse(options) { + var authToken = options.xhr.getResponseHeader(AUTHORIZATION_HEADER); + var prevToken = options.headers && options.headers[AUTHORIZATION_HEADER]; + if (authToken && authToken !== prevToken) { + if (authToken === 'null') { + localStorage.removeItem(STORAGE_AUTH_KEY); + } else { + localStorage.setItem(STORAGE_AUTH_KEY, authToken); + } + } +} + +function sync(baseUrl, method, options, data) { + + options = options ? _clone(options) : {}; + + var type = methodMap[method]; + _defaults(options || (options = {})); + var params = { + type: type, + dataType: 'json' + }; + params.url = composeURL(baseUrl, options); + + if ((method === 'create' || method === 'update') && data instanceof FormData) { + params.contentType = 'multipart/form-data'; + params.data = data; + } + else if (method === 'create' || method === 'update') { + params.contentType = 'application/json'; + params.data = JSON.stringify(data); + } + + if (params.type !== 'GET') { + params.processData = false; + } + var success = options.success; + options.success = function (resp) { + if (success) { + handleResponse(options); + success.call(options.context, _clone(resp), resp, options); + } + }; + + options.error = options.error || errorResponseHandler; + + if (typeof options.progressCallback === 'function' && options.fileSize) { + const {fileSize} = options; + options.xhrFields = { + // add listener to XMLHTTPRequest object directly for progress (jquery doesn't have this yet) + onprogress: function (progress) { + // calculate upload progress + let percentage = Math.floor((progress.loaded / fileSize) * 100); + // log upload progress to console + //console.log('progress', percentage); + options.progressCallback(percentage); + if (percentage === 100) { + console.log('DONE!'); + } + } + }; + } + + applySecurity(options, data); + + if (DEBUG) { + console.log('--> Making REST call (' + type + '): ' + params.url); + } + var xhr = options.xhr = $.ajax(_extend(params, options)); + return xhr; +} + +export default { + + fetch(baseUrl, options) { + return sync(baseUrl, 'read', options); + }, + + save(baseUrl, data, options) { + return sync(baseUrl, 'update', options, data); + }, + + create(baseUrl, data, options) { + return sync(baseUrl, 'create', options, data); + }, + + destroy(baseUrl, options) { + return sync(baseUrl, 'delete', options); + } + +}; diff --git a/openecomp-ui/src/nfvo-utils/UUID.js b/openecomp-ui/src/nfvo-utils/UUID.js new file mode 100644 index 0000000000..314c98ba6f --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/UUID.js @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import UUID from 'uuid-js'; + +let toCustomUUID = (uuid) => { + return 'U' + uuid.replace(/-/g, ''); +}; + +let getUUID = function(num, isSync) { + if (isSync) { + let uuid; + if (!num) { + uuid = toCustomUUID(UUID.create().toString()); + } else { + uuid = []; + for (var i = 0; i < num; i++) { + uuid[i] = toCustomUUID(UUID.create().toString()); + } + } + if (num === 1) { + return uuid[0]; + } else { + return uuid; + } + } + return new Promise(resolve => { + let uuid; + if (!num) { + uuid = toCustomUUID(UUID.create().toString()); + } else { + uuid = []; + for (var i = 0; i < num; i++) { + uuid[i] = toCustomUUID(UUID.create().toString()); + } + } + setTimeout(() => resolve(uuid), 100); + }); +}; + +export default getUUID; diff --git a/openecomp-ui/src/nfvo-utils/i18n/i18n.js b/openecomp-ui/src/nfvo-utils/i18n/i18n.js new file mode 100644 index 0000000000..64587713b7 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/i18n/i18n.js @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import IntlObj from 'intl'; +import IntlMessageFormatObj from 'intl-messageformat'; +import IntlRelativeFormatObj from 'intl-relativeformat'; +import createFormatCacheObj from 'intl-format-cache'; +import i18nJson from 'i18nJson'; + +/* + Intl libs are using out dated transpailer from ecmascript6. +* TODO: As soon as they fix it, remove this assignments!!! +* */ +var Intl = window.Intl || IntlObj.default, + IntlMessageFormat = IntlMessageFormatObj.default, + IntlRelativeFormat = IntlRelativeFormatObj.default, + createFormatCache = createFormatCacheObj.default; + +var i18nData; + +if(i18nJson) { + i18nData = i18nJson.dataWrapperArr[i18nJson.i18nDataIdx]; +} + + +/*extract locale*/ +var _locale = window.localStorage && localStorage.getItem('user_locale'); +if(!_locale) { + if(window.navigator) { + _locale = navigator.language || navigator.userLanguage; + + //For now removing the dashes from the language. + let indexOfDash = _locale.indexOf('-'); + if(-1 !== indexOfDash) { + _locale = _locale.substr(0, indexOfDash); + } + } + if(!_locale) { + _locale = 'en'; + } +} + +var _localeUpper = _locale.toUpperCase(); + +var i18n = { + + _locale: _locale, + _localeUpper: _localeUpper, + _i18nData: i18nData || {}, + + number(num) { + return createFormatCache(Intl.NumberFormat)(this._locale).format(num); + }, + + date(date, options, relativeDates) { + if (undefined === relativeDates || relativeDates) { + return this.dateRelative(date, options); + } else { + return this.dateNormal(date, options); + } + }, + + dateNormal(date, options) { + return createFormatCache(Intl.DateTimeFormat)(this._locale, options).format(date); + }, + + dateRelative(date, options) { + return createFormatCache(IntlRelativeFormat)(this._locale, options).format(date); + }, + + message(messageId, options) { + return createFormatCache(IntlMessageFormat)(this._i18nData[messageId] || String(messageId), this._locale).format(options); + }, + + getLocale() { + return this._locale; + }, + + getLocaleUpper() { + return this._localeUpper; + }, + + setLocale(locale) { + localStorage.setItem('user_locale', locale); + window.location.reload(); + } + +}; + +function i18nWrapper() { + return i18nWrapper.message.apply(i18nWrapper, arguments); +} + +/*replace with some kind of extend method*/ +var prop, propKey; +for (propKey in i18n) { + prop = i18n[propKey]; + if (typeof prop === 'function') { + prop = prop.bind(i18nWrapper); + } + i18nWrapper[propKey] = prop; +} + + +export default i18nWrapper; diff --git a/openecomp-ui/src/nfvo-utils/i18n/locale.json b/openecomp-ui/src/nfvo-utils/i18n/locale.json new file mode 100644 index 0000000000..d9047ba582 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/i18n/locale.json @@ -0,0 +1 @@ +{"dataWrapperArr":["I18N_IDENTIFIER_START",{},"I18N_IDENTIFIER_END"],"i18nDataIdx":1} \ No newline at end of file diff --git a/openecomp-ui/src/nfvo-utils/json/JSONPointer.js b/openecomp-ui/src/nfvo-utils/json/JSONPointer.js new file mode 100644 index 0000000000..a6e8198537 --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/json/JSONPointer.js @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +const JSONPointer = { + + extractParentPointer(pointer) { + return pointer.replace(/\/[^\/]+$/, ''); + }, + + extractLastPart(pointer) { + const [,lastPart] = pointer.match(/\/([^\/]+)$/) || []; + return lastPart; + }, + + extractParts(pointer) { + return pointer.split('/').slice(1) + .map(part => part.replace(/~1/g, '/')) + .map(part => part.replace(/~0/g, '~')); + }, + + getValue(object, pointer) { + let parts = JSONPointer.extractParts(pointer); + return parts.reduce((object, part) => object && object[part], object); + }, + + setValue(object, pointer, value) { + let clone = obj => Array.isArray(obj) ? [...obj] : {...obj}; + + let parts = JSONPointer.extractParts(pointer), + newObject = clone(object), + subObject = object, + subNewObject = newObject; + + for(let i = 0, n = parts.length - 1; i < n; ++i) { + let nextSubObject = subObject && subObject[parts[i]]; + subNewObject = subNewObject[parts[i]] = nextSubObject ? clone(nextSubObject) : {}; + subObject = nextSubObject; + } + subNewObject[parts[parts.length - 1]] = value; + + return newObject; + } +}; + +export default JSONPointer; diff --git a/openecomp-ui/src/nfvo-utils/json/JSONSchema.js b/openecomp-ui/src/nfvo-utils/json/JSONSchema.js new file mode 100644 index 0000000000..8c7d8cf9aa --- /dev/null +++ b/openecomp-ui/src/nfvo-utils/json/JSONSchema.js @@ -0,0 +1,155 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +// import Ajv from 'ajv'; +import cloneDeep from 'lodash/cloneDeep.js'; +import JSONPointer from './JSONPointer.js'; + +export default class JSONSchema { + + setSchema(schema) { + this._schema = schema; + this._fragmentsCache = new Map(); + // this._ajv = new Ajv({ + // useDefaults: true, + // coerceTypes: true + // }); + // this._validate = this._ajv.compile(schema); + } + + processData(data) { + data = cloneDeep(data); + // this._validate(data); + return data; + } + + getTitle(pointer) { + return this._getSchemaFragment(pointer).title; + } + + exists(pointer) { + const fragment = this._getSchemaFragment(pointer); + return !!fragment; + } + + getDefault(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.default; + } + + getEnum(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && (fragment.type === 'array' ? fragment.items.enum : fragment.enum); + } + + isRequired(pointer) { + const parentPointer = JSONPointer.extractParentPointer(pointer); + const lastPart = JSONPointer.extractLastPart(pointer); + let parentFragment = this._getSchemaFragment(parentPointer); + return parentFragment && parentFragment.required && parentFragment.required.includes(lastPart); + } + + isNumber(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.type === 'number'; + } + + getMaxValue(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.maximum; + } + + getMinValue(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.minimum; + } + + isString(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.type === 'string'; + } + + getPattern(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.pattern; + } + + getMaxLength(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.maxLength; + } + + getMinLength(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.minLength; + } + + isArray(pointer) { + const fragment = this._getSchemaFragment(pointer); + return fragment && fragment.type === 'array'; + } + + _getSchemaFragment(pointer) { + if (this._fragmentsCache.has(pointer)) { + return this._fragmentsCache.get(pointer); + } + + let parts = JSONPointer.extractParts(pointer); + + let fragment = parts.reduce((fragment, part) => { + if (fragment === undefined) { + return undefined; + } + + if (fragment.$ref) { + fragment = this._getSchemaFragmentByRef(fragment.$ref); + } + + switch (fragment.type) { + case 'object': + return fragment.properties && fragment.properties[part]; + + case 'array': + return fragment.enum && fragment.enum[part]; + + default: + // throw new Error(`Incorrect/unsupported JSONPointer "${pointer}" from "${part}"`); + return undefined; + } + }, this._schema); + + while(fragment && fragment.$ref) { + fragment = this._getSchemaFragmentByRef(fragment.$ref); + } + + this._fragmentsCache.set(pointer, fragment); + return fragment; + } + + _getSchemaFragmentByRef($ref) { + let pointer = $ref.substr(1); + return JSONPointer.getValue(this._schema, pointer); + // let fragmentAjv = new Ajv(); + // fragmentAjv.addSchema(this._schema); + // let compiledFragment = fragmentAjv.compile({$ref}); + // let fragment = compiledFragment.refVal[compiledFragment.refs[$ref]]; + // return fragment; + } +}; diff --git a/openecomp-ui/src/sdc-app/AppStore.js b/openecomp-ui/src/sdc-app/AppStore.js new file mode 100644 index 0000000000..0abcaac3fe --- /dev/null +++ b/openecomp-ui/src/sdc-app/AppStore.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {combineReducers, createStore} from 'redux'; +import onBoardingReducersMap from './onboarding/OnboardingReducersMap.js'; +import flowsReducersMap from './flows/FlowsReducersMap.js'; +import notificationReducer from 'nfvo-components/notifications/NotificationReducer.js'; +import loaderReducer from 'nfvo-components/loader/LoaderReducer.js'; +import uploadScreenReducer from 'sdc-app/heatvalidation/UploadScreenReducer.js'; +import SoftwareProductAttachmentsReducer from 'sdc-app/heatvalidation/attachments/AttachmentsReducer'; + +export const storeCreator = (initialState) => createStore(combineReducers({ + // on-boarding reducers + ...onBoardingReducersMap, + + // flows reducers + ...flowsReducersMap, + + // heat validation stand-alone app + uploadScreen: combineReducers({ + upload: uploadScreenReducer, + attachments: SoftwareProductAttachmentsReducer + }), + notification: notificationReducer, + loader: loaderReducer +}), initialState, window.devToolsExtension ? window.devToolsExtension() : undefined); + +const store = storeCreator(); + +export default store; diff --git a/openecomp-ui/src/sdc-app/Application.jsx b/openecomp-ui/src/sdc-app/Application.jsx new file mode 100644 index 0000000000..5cb385b61c --- /dev/null +++ b/openecomp-ui/src/sdc-app/Application.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import {Provider} from 'react-redux'; +import NotificationModal from 'nfvo-components/notifications/NotificationModal.jsx'; +import Loader from 'nfvo-components/loader/Loader.jsx'; +import store from './AppStore.js'; + + +class Application extends React.Component { + render() { + return ( + +
      + + {this.props.children} + +
      +
      + ); + } +} + +export default Application; + diff --git a/openecomp-ui/src/sdc-app/ModulesOptions.jsx b/openecomp-ui/src/sdc-app/ModulesOptions.jsx new file mode 100644 index 0000000000..4f66e579d1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/ModulesOptions.jsx @@ -0,0 +1,150 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import Input from 'react-bootstrap/lib/Input.js'; + +import LicenseModelActionHelper from './onboarding/licenseModel/LicenseModelActionHelper.js'; +import LicenseAgreementListEditor from './onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; +import LicenseAgreementActionHelper from './onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupListEditor from './onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js'; +import FeatureGroupsActionHelper from './onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; +import LicenseKeyGroupsListEditor from './onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js'; +import LicenseKeyGroupsActionHelper from './onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; +import EntitlementPoolsListEditor from './onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js'; +import EntitlementPoolsActionHelper from './onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; +import SoftwareProductLandingPage from './onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js'; +import SoftwareProductDetails from './onboarding/softwareProduct/details/SoftwareProductDetails.js'; +import OnboardingCatalog from './onboarding/OnboardingCatalog.js'; +import SoftwareProductActionHelper from './onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import FlowsListEditor from './flows/FlowsListEditor.js'; +import FlowsActions from './flows/FlowsActions.js'; + + +const mapStateToProps = ({licenseModelList}) => { + return {licenseModelList}; +}; + + +const mapActionsToProps = dispatch => { + return { + onBootstrapped: () => LicenseModelActionHelper.fetchLicenseModels(dispatch), + onLicenseAgreementListEditor: licenseModelId => LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId}), + onFeatureGroupsListEditor: licenseModelId => FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}), + onLicenseKeyGroupsListEditor: licenseModelId =>LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId}), + onEntitlementPoolsListEditor: licenseModelId => EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId}), + onOnboardingCatalog: () => SoftwareProductActionHelper.fetchSoftwareProductList(dispatch), + onSoftwareProductDetails: () => SoftwareProductActionHelper.fetchSoftwareProductCategories(dispatch), + onFlowsListEditor: () => FlowsActions.fetchFlows(dispatch) + }; +}; + +class ModuleOptions extends React.Component { + + static propTypes = { + onBootstrapped: React.PropTypes.func.isRequired, + onLicenseAgreementListEditor: React.PropTypes.func.isRequired, + onFeatureGroupsListEditor: React.PropTypes.func.isRequired, + onLicenseKeyGroupsListEditor: React.PropTypes.func.isRequired, + onEntitlementPoolsListEditor: React.PropTypes.func.isRequired, + onOnboardingCatalog: React.PropTypes.func.isRequired, + onSoftwareProductDetails: React.PropTypes.func.isRequired, + }; + + state = { + currentModule: localStorage.getItem('default-module'), + licenseModelId: localStorage.getItem('default-license-model-id') + }; + + componentDidMount() { + this.props.onBootstrapped(); + } + + render() { + let {currentModule, licenseModelId} = this.state; + let {licenseModelList} = this.props; + return ( +
      + + + { + licenseModelList.map(({id, vendorName}) => ) + } + + + + + + + + + + + + +
      + {this.renderModule(currentModule)} +
      +
      + ); + } + + renderModule(currentModule) { + const {licenseModelId} = this.state; + if (!licenseModelId) { + return; + } + + switch (currentModule) { + case 'LicenseAgreementListEditor': + this.props.onLicenseAgreementListEditor(licenseModelId); + return ; + case 'FutureGroupListEditor': + this.props.onFeatureGroupsListEditor(licenseModelId); + return ; + case 'EntitlementPoolsListEditor': + this.props.onEntitlementPoolsListEditor(licenseModelId); + return ; + case 'LicenseKeyGroupsListEditor': + this.props.onLicenseKeyGroupsListEditor(licenseModelId); + return ; + case 'SoftwareProductLanding': + return ; + case 'SoftwareProductDetails': + this.props.onSoftwareProductDetails(licenseModelId); + return ; + case 'OnboardingCatalog': + this.props.onOnboardingCatalog(); + return ; + case 'Flows': + this.props.onFlowsListEditor(); + return ; + default: + return; + } + } + + handleModuleSelection = () => { + let selectedModule = this.refs.selectedModule.getValue(); + localStorage.setItem('default-module', selectedModule); + this.setState({currentModule: selectedModule}); + } + + handleLicenseModelIdChange = () => { + let licenseModelId = this.refs.licenseModelId.getValue(); + localStorage.setItem('default-license-model-id', licenseModelId); + this.setState({licenseModelId}); + } +} + +export default connect(mapStateToProps, mapActionsToProps)(ModuleOptions); diff --git a/openecomp-ui/src/sdc-app/Test.jsx b/openecomp-ui/src/sdc-app/Test.jsx new file mode 100644 index 0000000000..dd45e39eca --- /dev/null +++ b/openecomp-ui/src/sdc-app/Test.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import Tabs from 'react-bootstrap/lib/Tabs.js'; +import Tab from 'react-bootstrap/lib/Tab.js'; +import Button from 'react-bootstrap/lib/Button.js'; +import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; +import DropdownButton from 'react-bootstrap/lib/DropdownButton.js'; +import MenuItem from 'react-bootstrap/lib/MenuItem.js'; + +import Modal from 'nfvo-components/modal/Modal.jsx'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import ToggleInput from 'nfvo-components/input/ToggleInput.jsx'; + +export default class Test extends React.Component { + + render() { + return ( +
      + + Tab 1 content + Tab 2 content + Tab 3 content + +
      + + + + + + + + + + + + + +
      + + + + + +
      + + Action + Another action + Active Item + + Separated link + + +
      + + + Modal title + + + + One fine body... + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + +
      + ); + } + + doSomething(a) { + if (a) { + this.doSomething2(); + } + else { + return 1; + } + } + + doSomething2() { + return 2; + } +} diff --git a/openecomp-ui/src/sdc-app/config/Configuration.js b/openecomp-ui/src/sdc-app/config/Configuration.js new file mode 100644 index 0000000000..4bbe07864f --- /dev/null +++ b/openecomp-ui/src/sdc-app/config/Configuration.js @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import configData from './config.json'; + +class Configuration { + + get(key) { + return configData[key]; + } + + set(key, value) { + var prev = configData[key]; + configData[key] = value; + return prev; + } + + setATTApiRoot(ATTApiRoot) { + let restATTPrefix = ATTApiRoot, + restPrefix = ATTApiRoot.replace(/\/feProxy\b[^:]*$/, '/feProxy/onboarding-api'); + + this.set('restPrefix', restPrefix); + this.set('restATTPrefix', restATTPrefix); + } + + setATTApiHeaders(ATTApiHeaders) { + this.set('ATTApiHeaders', ATTApiHeaders); + + let {userId: {value: ATTUserID} = {}} = ATTApiHeaders; + this.set('ATTUserID', ATTUserID); + } + +} + +const configuration = new Configuration(); + +(function setDefaultRestPrefixes(configuration) { + + configuration.set('restPrefix', configuration.get('defaultRestPrefix')); + configuration.set('restATTPrefix', configuration.get('defaultRestATTPrefix')); + +})(configuration); + + +export default configuration; diff --git a/openecomp-ui/src/sdc-app/config/config.json b/openecomp-ui/src/sdc-app/config/config.json new file mode 100644 index 0000000000..4127e0c12e --- /dev/null +++ b/openecomp-ui/src/sdc-app/config/config.json @@ -0,0 +1,8 @@ +{ + "pageSize": 25, + "version": "9.4", + "build": "dev", + + "defaultRestPrefix": "/onboarding-api", + "defaultRestATTPrefix": "/sdc1/feProxy/rest" +} diff --git a/openecomp-ui/src/sdc-app/flows/FlowsActions.js b/openecomp-ui/src/sdc-app/flows/FlowsActions.js new file mode 100644 index 0000000000..b8772edb08 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsActions.js @@ -0,0 +1,191 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes, enums} from './FlowsConstants.js'; +import SequenceDiagramModelHelper from './SequenceDiagramModelHelper.js'; + + +function baseUrl(serviceId, artifactId = '') { + const restATTPrefix = Configuration.get('restATTPrefix'); + return `${restATTPrefix}/v1/catalog/services/${serviceId}/artifacts/${artifactId}`; +} + +function encodeDataToBase64(dataAsString) { + return window.btoa(dataAsString); +} + +function decodeDataToBase64(encodedData) { + return window.atob(encodedData); +} + +function encodeContent(flowData) { + let data = { + VERSION: { + major: 1, + minor: 0 + }, + description: flowData.description, + sequenceDiagramModel: flowData.sequenceDiagramModel + }; + + return encodeDataToBase64(JSON.stringify(data)); +} + +function decodeContent(base64Contents) { + let description, sequenceDiagramModel; + let payload = JSON.parse(decodeDataToBase64(base64Contents)); + + if (payload.VERSION === undefined) { + description = payload.description || 'Please, provide description...'; + sequenceDiagramModel = payload.data || payload; + sequenceDiagramModel = sequenceDiagramModel.model || sequenceDiagramModel; + + } else if (payload.VERSION.major === 1) { + description = payload.description; + sequenceDiagramModel = payload.sequenceDiagramModel; + } + + return { + description, + sequenceDiagramModel + }; +} + +function createOrUpdate(flowData) { + let createOrUpdateRequest = { + payloadData: encodeContent(flowData), + artifactLabel: flowData.artifactLabel || flowData.artifactName, + artifactName: flowData.artifactName, + artifactType: flowData.artifactType, + artifactGroupType: enums.INFORMATIONAL, + description: flowData.description + }; + + return RestAPIUtil.create( + baseUrl(flowData.serviceID, flowData.uniqueId), + createOrUpdateRequest, + {md5: true} + ); +} + +const FlowsActions = Object.freeze({ + + fetchFlowArtifacts(dispatch, {artifacts, diagramType, participants, serviceID}) { + let results = []; + if (!Object.keys(artifacts).length) { + dispatch({type: actionTypes.FLOW_LIST_LOADED, results, participants, serviceID, diagramType}); + FlowsActions.openFlowDetailsEditor(dispatch); + } + else { + Object.keys(artifacts).forEach(artifact => results.push({ + artifactType: diagramType, + participants, + serviceID, + ...artifacts[artifact] + })); + dispatch({type: actionTypes.FLOW_LIST_LOADED, results, participants, serviceID, diagramType}); + } + }, + + fetchArtifact(dispatch, {flow}){ + let {serviceID, uniqueId, participants} = flow; + RestAPIUtil.fetch(baseUrl(serviceID, uniqueId)).then(response => { + + let {artifactName, base64Contents} = response; + let {sequenceDiagramModel, ...other} = decodeContent(base64Contents); + + if (!sequenceDiagramModel) { + sequenceDiagramModel = SequenceDiagramModelHelper.createModel({ + id: uniqueId, + name: artifactName, + lifelines: participants + }); + } + else { + sequenceDiagramModel = SequenceDiagramModelHelper.updateModel(sequenceDiagramModel, { + name: artifactName, + lifelines: participants + }); + } + + flow = { + ...flow, + ...other, + uniqueId, + artifactName, + sequenceDiagramModel + }; + + dispatch({type: actionTypes.ARTIFACT_LOADED, flow}); + FlowsActions.openFlowDiagramEditor(dispatch, {flow}); + }); + }, + + createOrUpdateFlow(dispatch, {flow}, isNew) { + if (!isNew && flow.sequenceDiagramModel) { + flow.sequenceDiagramModel = SequenceDiagramModelHelper.updateModel(flow.sequenceDiagramModel, { + name: flow.artifactName + }); + } + createOrUpdate(flow).then(response => { + let {uniqueId, artifactLabel} = response; + flow = {...flow, uniqueId, artifactLabel}; + if (isNew) { + flow.sequenceDiagramModel = SequenceDiagramModelHelper.createModel({id: uniqueId, name: flow.artifactName}); + } + dispatch({type: actionTypes.ADD_OR_UPDATE_FLOW, flow}); + }); + }, + + deleteFlow(dispatch, {flow}) { + RestAPIUtil.destroy(baseUrl(flow.serviceID, flow.uniqueId)).then(() => dispatch({ + type: actionTypes.DELETE_FLOW, + flow + })); + }, + + openFlowDetailsEditor(dispatch, flow) { + dispatch({type: actionTypes.OPEN_FLOW_DETAILS_EDITOR, flow}); + }, + + closeFlowDetailsEditor(dispatch) { + dispatch({type: actionTypes.CLOSE_FLOW_DETAILS_EDITOR}); + }, + + openFlowDiagramEditor(dispatch, {flow}) { + dispatch({type: actionTypes.OPEN_FLOW_DIAGRAM_EDITOR, flow}); + }, + + closeFlowDiagramEditor(dispatch) { + dispatch({type: actionTypes.CLOSE_FLOW_DIAGRAM_EDITOR}); + }, + + flowDetailsDataChanged(dispatch, {deltaData}) { + dispatch({type: actionTypes.CURRENT_FLOW_DATA_CHANGED, deltaData}); + }, + + reset(dispatch) { + dispatch({type: actionTypes.RESET}); + } +}); + +export default FlowsActions; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsConstants.js b/openecomp-ui/src/sdc-app/flows/FlowsConstants.js new file mode 100644 index 0000000000..5a43a4df4f --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsConstants.js @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + + OPEN_FLOW_DETAILS_EDITOR: null, + CLOSE_FLOW_DETAILS_EDITOR: null, + + OPEN_FLOW_DIAGRAM_EDITOR: null, + CLOSE_FLOW_DIAGRAM_EDITOR: null, + + FLOW_LIST_LOADED: null, + ADD_OR_UPDATE_FLOW: null, + ARTIFACT_LOADED: null, + DELETE_FLOW: null, + + CURRENT_FLOW_DATA_CHANGED: null, + + RESET: null + +}); + +export const enums = { + WORKFLOW: 'WORKFLOW', + NETWORK: 'NETWORK_CALL_FLOW', + INFORMATIONAL: 'INFORMATIONAL', + INSTANTIATION_FLOWS: 'instantiationflows', + MESSAGE_FLOWS: 'messageflows' +}; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js b/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js new file mode 100644 index 0000000000..eff1c36b80 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsEditorModal.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import FlowsEditorModalView from './FlowsEditorModalView.jsx'; +import FlowsActions from './FlowsActions.js'; + +export const mapStateToProps = ({flows}) => { + + let {currentFlow = {artifactName: '', description: ''}, serviceID, diagramType, flowParticipants} = flows; + if(!currentFlow.serviceID){ + currentFlow.serviceID = serviceID; + } + if(!currentFlow.artifactType){ + currentFlow.artifactType = diagramType; + } + if(!currentFlow.participants){ + currentFlow.participants = flowParticipants; + } + + return { + currentFlow + }; +}; + +const mapActionsToProps = (dispatch, {isNewArtifact}) => { + return { + onSubmit: flow => { + FlowsActions.closeFlowDetailsEditor(dispatch); + FlowsActions.createOrUpdateFlow(dispatch, {flow}, isNewArtifact); + }, + onCancel: () => FlowsActions.closeFlowDetailsEditor(dispatch), + onDataChanged: deltaData => FlowsActions.flowDetailsDataChanged(dispatch, {deltaData}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(FlowsEditorModalView); diff --git a/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx b/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx new file mode 100644 index 0000000000..8441c7d1d6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsEditorModalView.jsx @@ -0,0 +1,40 @@ +import React, {Component} from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Input from 'nfvo-components/input/validation/ValidationInput.jsx'; +import Form from 'nfvo-components/input/validation/ValidationForm.jsx'; + +class FlowsEditorModalView extends Component { + + render() { + let {onCancel, onDataChanged, currentFlow} = this.props; + let {artifactName, description} = currentFlow; + return ( +
      this.onSaveClicked()} onReset={onCancel}> + onDataChanged({artifactName})}/> + onDataChanged({description})}/> +
      + ); + } + + onSaveClicked() { + let {currentFlow, onSubmit} = this.props; + if (onSubmit) { + onSubmit(currentFlow); + } + } + +} + +export default FlowsEditorModalView; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js b/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js new file mode 100644 index 0000000000..ff301b6e13 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsListEditor.js @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import FlowsActions from './FlowsActions.js'; +import FlowsListEditorView from './FlowsListEditorView.jsx'; + +export const mapStateToProps = ({flows}) => { + let {flowList = [], isDisplayModal, isModalInEditMode, shouldShowWorkflowsEditor = true, currentFlow = undefined} = flows; + let isCheckedOut = currentFlow ? !currentFlow.readonly : true; + + return { + flowList, + isDisplayModal, + isCheckedOut, + isModalInEditMode, + shouldShowWorkflowsEditor, + currentFlow + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onAddWorkflowClick: () => FlowsActions.openFlowDetailsEditor(dispatch), + onEditFlowDetailsClick: flow => FlowsActions.openFlowDetailsEditor(dispatch, flow), + onEditFlowDiagramClick: flow => FlowsActions.fetchArtifact(dispatch, {flow}), + onDeleteFlowClick: flow => FlowsActions.deleteFlow(dispatch, {flow}), + onSequenceDiagramSaveClick: flow => { + FlowsActions.closeFlowDiagramEditor(dispatch); + FlowsActions.createOrUpdateFlow(dispatch, {flow}); + }, + onSequenceDiagramCloseClick: () => FlowsActions.closeFlowDiagramEditor(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(FlowsListEditorView); diff --git a/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx b/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx new file mode 100644 index 0000000000..3cea3968ff --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsListEditorView.jsx @@ -0,0 +1,133 @@ +import React, {PropTypes, Component} from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import FlowRelatedView from './ImportantLogic.jsx'; +import FlowsEditorModal from './FlowsEditorModal.js'; +import SequenceDiagram from './SequenceDiagram.jsx'; + +class FlowsListEditorView extends Component { + + static propTypes = { + flowList: PropTypes.array, + currentFlow: PropTypes.object, + isDisplayModal: PropTypes.bool, + isModalInEditMode: PropTypes.bool, + isCheckedOut: PropTypes.bool, + shouldShowWorkflowsEditor: PropTypes.bool, + + onAddWorkflowClick: PropTypes.func, + onEditFlowDetailsClick: PropTypes.func, + onEditFlowDiagramClick: PropTypes.func, + onDeleteFlowClick: PropTypes.func, + onSequenceDiagramSaveClick: PropTypes.func, + onSequenceDiagramCloseClick: PropTypes.func + }; + + state = { + localFilter: '' + }; + + render() { + let CurrentView = null; + if (this.props.shouldShowWorkflowsEditor) { + CurrentView = this.renderWorkflowsEditor(); + } + else { + CurrentView = this.renderSequenceDiagramTool(); + } + + return CurrentView; + } + + renderWorkflowsEditor() { + let {isDisplayModal, onAddWorkflowClick, isCheckedOut} = this.props; + const {localFilter} = this.state; + + return ( +
      + + this.setState({localFilter: filter})} + isCheckedOut={isCheckedOut}> + {this.filterList().map(flow => this.renderWorkflowListItem(flow, isCheckedOut))} + + + {isDisplayModal && this.renderWorkflowEditorModal()} + +
      + ); + } + + renderWorkflowEditorModal() { + let { isDisplayModal, isModalInEditMode} = this.props; + return ( + + + + {`${isModalInEditMode ? i18n('Edit Workflow') : i18n('Create New Workflow')}`} + + + + + + + ); + } + + renderSequenceDiagramTool() { + let {onSequenceDiagramSaveClick, onSequenceDiagramCloseClick, currentFlow} = this.props; + return ( + onSequenceDiagramSaveClick({...currentFlow, sequenceDiagramModel})} + onClose={onSequenceDiagramCloseClick} + model={currentFlow.sequenceDiagramModel}/> + ); + } + + filterList() { + let {flowList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return flowList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return flowList; + } + } + + renderWorkflowListItem(flow, isCheckedOut) { + let {uniqueId, artifactName, description} = flow; + let {onEditFlowDetailsClick, onEditFlowDiagramClick, onDeleteFlowClick} = this.props; + return ( + onEditFlowDetailsClick(flow)} + onDelete={() => onDeleteFlowClick(flow)} + onEdit={() => onEditFlowDiagramClick(flow)} + className='list-editor-item-view' + isCheckedOut={isCheckedOut}> +
      +
      {i18n('Name')}
      +
      {artifactName}
      +
      +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      + ); + } + +} + +export default FlowsListEditorView; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js b/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js new file mode 100644 index 0000000000..f025450a58 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsListReducer.js @@ -0,0 +1,97 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './FlowsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.FLOW_LIST_LOADED: + return { + ...state, + flowList: action.results, + flowParticipants: action.participants, + serviceID: action.serviceID, + diagramType: action.diagramType + }; + case actionTypes.ADD_OR_UPDATE_FLOW: + case actionTypes.ARTIFACT_LOADED: + let flowList = state.flowList || []; + let index = flowList.findIndex(flow => flow.uniqueId === action.flow.uniqueId); + if (index === -1) { + index = flowList.length; + } + let flowToBeUpdated = flowList[index]; + flowList = [ + ...flowList.slice(0, index), + {...flowToBeUpdated, ...action.flow}, + ...flowList.slice(index + 1) + ]; + return { + ...state, + flowList, + serviceID: action.flow.serviceID, + diagramType: action.flow.artifactType || state.diagramType + }; + case actionTypes.CURRENT_FLOW_DATA_CHANGED: + return { + ...state, + currentFlow: { + ...state.currentFlow, + ...action.deltaData + } + }; + case actionTypes.DELETE_FLOW: + return { + ...state, + flowList: state.flowList.filter(flow => flow.uniqueId !== action.flow.uniqueId) + }; + case actionTypes.OPEN_FLOW_DETAILS_EDITOR: + return { + ...state, + currentFlow: action.flow, + isDisplayModal: true, + isModalInEditMode: Boolean(action.flow && action.flow.uniqueId) + }; + + case actionTypes.CLOSE_FLOW_DETAILS_EDITOR: + return { + ...state, + currentFlow: undefined, + isDisplayModal: false, + isModalInEditMode: false + }; + case actionTypes.OPEN_FLOW_DIAGRAM_EDITOR: + return { + ...state, + currentFlow: action.flow, + shouldShowWorkflowsEditor: false + }; + case actionTypes.CLOSE_FLOW_DIAGRAM_EDITOR: + return { + ...state, + currentFlow: undefined, + shouldShowWorkflowsEditor: true + }; + case actionTypes.RESET: + return {}; + } + + return state; +}; diff --git a/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx b/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx new file mode 100644 index 0000000000..958f9a0a2d --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsPunchOut.jsx @@ -0,0 +1,63 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Configuration from 'sdc-app/config/Configuration.js'; +import Application from 'sdc-app/Application.jsx'; +import store from 'sdc-app/AppStore.js'; +import FlowsListEditor from './FlowsListEditor.js'; +import FlowsActions from './FlowsActions.js'; + +class FlowsListEditorPunchOutWrapper extends React.Component { + + componentDidMount() { + let element = ReactDOM.findDOMNode(this); + element.addEventListener('click', event => { + if (event.target.tagName === 'A') { + event.preventDefault(); + } + }); + ['wheel', 'mousewheel', 'DOMMouseScroll'].forEach(eventType => + element.addEventListener(eventType, event => event.stopPropagation()) + ); + } + + render() { + return ; + } +} + +export default class DiagramPunchOut { + + render({options: {data, apiRoot, apiHeaders}, onEvent}, element) { + + if (!this.isConfigSet) { + Configuration.setATTApiRoot(apiRoot); + Configuration.setATTApiHeaders(apiHeaders); + this.isConfigSet = true; + } + + this.onEvent = onEvent; + this.handleData(data); + + if (!this.rendered) { + ReactDOM.render(
      , element); + this.rendered = true; + } + } + + unmount(element) { + let dispatch = action => store.dispatch(action); + ReactDOM.unmountComponentAtNode(element); + FlowsActions.reset(dispatch); + } + + handleData(data) { + let {serviceID, diagramType} = data; + let dispatch = action => store.dispatch(action); + + if (serviceID !== this.prevServiceID || diagramType !== this.prevDiagramType) { + this.prevServiceID = serviceID; + this.prevDiagramType = diagramType; + FlowsActions.fetchFlowArtifacts(dispatch, {...data}); + } + } +} diff --git a/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js b/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js new file mode 100644 index 0000000000..b3c0b2e27b --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/FlowsReducersMap.js @@ -0,0 +1,25 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import flowListReducer from './FlowsListReducer.js'; + +export default { + flows: flowListReducer +}; diff --git a/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx b/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx new file mode 100644 index 0000000000..c4ab41841b --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/ImportantLogic.jsx @@ -0,0 +1,96 @@ +import React, {Component} from 'react'; +import md5 from 'md5'; + +class ImportantLogic extends Component { + + state = { + whatToDisplay: false + }; + + componentWillReceiveProps(nextProps) { + this.setState({whatToDisplay: md5(nextProps.display) === 'a55899b341525123628776dbf5755d51'}); + } + + render() { + if (this.state.whatToDisplay) { + setTimeout(() => this.setState({whatToDisplay: false}), 5000); + } + + return ( +
      + +
      + {} + {} +
      +
      + ); + } +} + +export default ImportantLogic; diff --git a/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx b/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx new file mode 100644 index 0000000000..9970969884 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/SequenceDiagram.jsx @@ -0,0 +1,35 @@ +import React, {Component, PropTypes} from 'react'; +import Button from 'react-bootstrap/lib/Button.js'; +import Sequencer from 'dox-sequence-diagram-ui'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +class SequenceDiagram extends Component { + + static propTypes = { + onSave: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + model: PropTypes.object.isRequired + }; + + onSave() { + this.props.onSave(this.refs.sequencer.getModel()); + } + + render() { + return ( +
      +
      + +
      +
      + + +
      +
      + ); + } + +} + +export default SequenceDiagram; diff --git a/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js b/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js new file mode 100644 index 0000000000..c2e10a6360 --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/SequenceDiagramModelHelper.js @@ -0,0 +1,71 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import emptyModel from './emptyModel.json'; + +function mergeLifelines(oldLifelines, newLifelines) { + let oldLifelinesMap = new Map(oldLifelines.map(lifeline => [lifeline.id, lifeline])); + let newLifelinesMap = new Map(newLifelines.map(lifeline => [lifeline.id, lifeline])); + + let updatedLifelines = oldLifelines.map(lifeline => { + let newLifeline = newLifelinesMap.get(lifeline.id); + return { + ...lifeline, + name: newLifeline ? newLifeline.name : lifeline.name + }; + }); + + let addedLifelines = newLifelines.filter(lifeline => !oldLifelinesMap.has(lifeline.id)); + + return [ + ...updatedLifelines, + ...addedLifelines + ]; +} + + +const SequenceDiagramModelHelper = Object.freeze({ + + createModel(options) { + return SequenceDiagramModelHelper.updateModel(emptyModel, options); + }, + + updateModel(model, options) { + const diagram = model.diagram; + const metadata = diagram.metadata || model.metadata; + const id = options.id || metadata.id; + const name = options.name || metadata.name; + const lifelines = options.lifelines ? mergeLifelines(diagram.lifelines, options.lifelines) : diagram.lifelines; + + return { + diagram: { + ...diagram, + metadata: { + ...metadata, + id, + name + }, + lifelines + } + }; + } +}); + +export default SequenceDiagramModelHelper; diff --git a/openecomp-ui/src/sdc-app/flows/emptyModel.json b/openecomp-ui/src/sdc-app/flows/emptyModel.json new file mode 100644 index 0000000000..20f4b2fe3c --- /dev/null +++ b/openecomp-ui/src/sdc-app/flows/emptyModel.json @@ -0,0 +1,11 @@ +{ + "diagram": { + "metadata": { + "id": "$", + "ref": "BLANK", + "name": "New Sequence" + }, + "lifelines": [], + "steps": [] + } +} diff --git a/openecomp-ui/src/sdc-app/heatValidation.app.jsx b/openecomp-ui/src/sdc-app/heatValidation.app.jsx new file mode 100644 index 0000000000..eb58a79b25 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatValidation.app.jsx @@ -0,0 +1,13 @@ +import '../../resources/scss/bootstrap.scss'; +import '../../resources/css/font-awesome.min.css'; +import 'react-select/dist/react-select.min.css'; +import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; +import '../../resources/scss/style.scss'; + +import React from 'react'; +import ReactDOM from 'react-dom'; +import UploadScreen from './heatvalidation/UploadScreen.jsx'; +import Application from './Application.jsx'; + + +ReactDOM.render(, document.getElementById('heat-validation-app')); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx new file mode 100644 index 0000000000..0bb496fc51 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreen.jsx @@ -0,0 +1,182 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import Button from 'react-bootstrap/lib/Button.js'; +import Dropzone from 'react-dropzone'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ProgressBar from 'nfvo-components/progressBar/ProgressBar.jsx'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import UploadScreenActionHelper from './UploadScreenActionHelper.js'; +import Attachments from './attachments/Attachments.js'; + +const mapStateToProps = ({uploadScreen}) => { + let {upload} = uploadScreen; + return {uploadScreen: upload}; +}; + + +const mapActionsToProps = dispatch => { + return { + onUpload: (formData) => UploadScreenActionHelper.uploadFile(dispatch, formData), + openMainScreen: () => UploadScreenActionHelper.openMainScreen(dispatch) + }; +}; + + +class UploadScreen extends React.Component { + + state = { + complete: '10', + showModal: false, + fileName: '', + dragging: false, + files: [] + }; + + interval = ''; + + render() { + let {uploadScreen} = this.props; + let {showAttachments} = uploadScreen; + return( +
      + {showAttachments ? this.renderTree() : this.renderUploadScreen()} +
      + ); + } + + renderUploadModal() { + let {complete, showModal, fileName} = this.state; + return ( + + + {i18n('Uploading attachments')} + + +
      +
      + {i18n('File:')} + {fileName} +
      + +
      {i18n('Upload in progress')}
      +
      + + + + +
      +
      + ); + } + + renderUploadScreen() { + return( +
      +
      +
      +

      HEAT VALIDATION APPLICATION

      +
      +
      +
      +
      + this.handleImportSubmit(files)} + onDragEnter={() => this.setState({dragging:true})} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='fileInput' + name='fileInput' + accept='.zip'> +
      +
      {i18n('Drag & drop for upload')}
      +
      {i18n('or')}
      +
      this.refs.fileInput.open()}> + {i18n('Select file')} +
      +
      +
      +
      + {this.renderUploadModal()} +
      +
      + ); + } + + renderTree() { + let {openMainScreen} = this.props; + return( +
      + +
      +
      openMainScreen()}> + {i18n('Back')} +
      +
      +
      + ); + } + + handleImportSubmit(files) { + this.setState({ + showModal: true, + fileName: files[0].name, + dragging: false, + complete: '0', + files + }); + + + this.interval = setInterval(() => { + if (this.state.complete >= 90) { + clearInterval(this.interval); + this.setState({ + showModal: false, + fileName: '' + }); + this.startUploading(files); + } else { + this.setState({ + complete: (parseInt(this.state.complete) + 10).toString() + }); + } + }, 20); + + } + + onRunBackground() { + let {files} = this.state; + clearInterval(this.interval); + this.startUploading(files); + this.setState({showModal: false, files: []}); + } + + onCancel() { + clearInterval(this.interval); + this.setState({ + showModal: false, + fileName: '', + files: [] + }); + + } + + startUploading(files) { + let {onUpload} = this.props; + if (!(files && files.length)) { + return; + } + let file = files[0]; + let formData = new FormData(); + formData.append('upload', file); + this.refs.fileInput.value = ''; + onUpload(formData); + } + +} + +export default connect(mapStateToProps, mapActionsToProps)(UploadScreen); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js new file mode 100644 index 0000000000..3b8de0f0d4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenActionHelper.js @@ -0,0 +1,60 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import {actionTypes} from './UploadScreenConstants.js'; +import {actionTypes as softwareProductsActionTypes} from '../onboarding/softwareProduct/SoftwareProductConstants.js'; + +function uploadFile(formData) { + return RestAPIUtil.create('/sdc1/feProxy/onboarding-api/v1.0/validation/HEAT/validate', formData); +} + +const UploadScreenActionHelper = { + uploadFile(dispatch, formData) { + + + Promise.resolve() + .then(() => uploadFile(formData)) + .then(response => { + dispatch({ + type: softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + + dispatch({ + type: actionTypes.OPEN_UPLOAD_SCREEN + }); + }) + .catch(error => { + dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {title: 'File Upload Failed', msg: error.responseJSON.message} + }); + }); + }, + openMainScreen(dispatch) { + dispatch({ + type: actionTypes.OPEN_MAIN_SCREEN + }); + } +}; + +export default UploadScreenActionHelper; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenConstants.js b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenConstants.js new file mode 100644 index 0000000000..2766a975ec --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenConstants.js @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + FILE_UPLOADED: null, + OPEN_UPLOAD_SCREEN: null, + OPEN_ATTACHMENTS_SCREEN: null, + OPEN_MAIN_SCREEN: null +}); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenReducer.js b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenReducer.js new file mode 100644 index 0000000000..e73e028233 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/UploadScreenReducer.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './UploadScreenConstants.js'; + + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.OPEN_UPLOAD_SCREEN: + return {...state, showAttachments: true}; + case actionTypes.OPEN_MAIN_SCREEN: + return {...state, showAttachments: false}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js new file mode 100644 index 0000000000..2a6a992844 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import AttachmentsView from './AttachmentsView.jsx'; +import AttachmentsActionHelper from './AttachmentsActionHelper.js'; + + +const mapStateToProps = ({uploadScreen: {attachments}}) => { + let {attachmentsTree = false, hoveredNode, selectedNode, errorList} = attachments; + return { + attachmentsTree, + hoveredNode, + selectedNode, + errorList + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + + toggleExpanded: (path) => AttachmentsActionHelper.toggleExpanded(dispatch, {path}), + onSelectNode: (nodeName) => AttachmentsActionHelper.onSelectNode(dispatch, {nodeName}), + onUnselectNode: () => AttachmentsActionHelper.onUnselectNode(dispatch), + + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(AttachmentsView); diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js new file mode 100644 index 0000000000..15b0ffa4a9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './AttachmentsConstants.js'; + +export default { + + toggleExpanded(dispatch, {path}) { + dispatch({ + type: actionTypes.TOGGLE_EXPANDED, + path + }); + }, + + onSelectNode(dispatch, {nodeName}) { + dispatch({ + type: actionTypes.SELECTED_NODE, + nodeName + }); + }, + + onUnselectNode(dispatch) { + dispatch({ + type: actionTypes.UNSELECTED_NODE + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js new file mode 100644 index 0000000000..33af476d9c --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + TOGGLE_EXPANDED: null, + SELECTED_NODE: null, + UNSELECTED_NODE: null +}); + +export const errorTypes = keyMirror({ + MISSING_FILE_IN_ZIP: i18n('missing file in zip'), + MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'), + MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'), + FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'), + FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'), + ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'), + ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'), + MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'), + MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'), + ARTIFACT_NOT_IN_USE: i18n('artifact not in use') +}); + +export const nodeTypes = keyMirror({ + heat: i18n('Heat'), + volume: i18n('Volume'), + network: i18n('Network'), + artifact: i18n('Artifact'), + env: i18n('Environment'), + other: i18n('') +}); + +export const mouseActions = keyMirror({ + MOUSE_BUTTON_CLICK: 0 +}); + diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js new file mode 100644 index 0000000000..01f68aede8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js @@ -0,0 +1,199 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes} from './AttachmentsConstants.js'; + +const mapVolumeData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'volume', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapNetworkData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'network', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapArtifactsData = ({fileName, errors}) => ({ + name: fileName, + type: 'artifact', + errors +}); + +const mapOtherData = ({fileName, errors}) => ({ + name: fileName, + type: 'other', + errors +}); + + +const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({ + name: fileName, + expanded: true, + type: 'heat', + errors, + children: [ + ...(volume ? volume.map(mapVolumeData) : []), + ...(network ? network.map(mapNetworkData) : []), + ...(env ? [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }] : []), + ...(artifacts ? artifacts.map(mapArtifactsData) : []), + ...(other ? other.map(mapOtherData) : []), + ...(nested ? nested.map(mapHeatData) : []) + ] +}); + +function createErrorList(node, parent, deep = 0, errorList = []) { + if (node.errors) { + errorList.push(...node.errors.map((error) => ({ + errorLevel: error.level, + errorMessage: error.message, + name: node.name, + hasParent: deep > 2, + parentName: parent.name, + type: node.type, + }))); + } + if (node.children && node.children.length) { + node.children.map((child) => createErrorList(child, node, deep + 1, errorList)); + } + return errorList; +} + +const mapValidationDataToTree = validationData => { + let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {}; + return { + children: [ + { + name: 'HEAT', + expanded: true, + type: 'heat', + children: (HEAT ? HEAT.map(mapHeatData) : []) + }, + ...(artifacts ? [{ + name: 'artifacts', + expanded: true, + type: 'artifact', + children: (artifacts ? artifacts.map(mapArtifactsData) : []) + }] : []), + ...(network ? [{ + name: 'networks', + expanded: true, + type: 'network', + children: (network ? network.map(mapNetworkData) : []), + }] : []), + ...(volume ? [{ + name: 'volume', + expanded: true, + type: 'volume', + children: (volume ? volume.map(mapVolumeData) : []), + }] : []), + ...(other ? [{ + name: 'other', + expanded: true, + type: 'other', + children: (other ? other.map(mapOtherData) : []), + }] : []) + ] + }; +}; + +const toggleExpanded = (node, path) => { + let newNode = {...node}; + if (path.length === 0) { + newNode.expanded = !node.expanded; + } else { + let index = path[0]; + newNode.children = [ + ...node.children.slice(0, index), + toggleExpanded(node.children[index], path.slice(1)), + ...node.children.slice(index + 1) + ]; + } + return newNode; +}; + +const expandSelected = (node, selectedNode) => { + let shouldExpand = node.name === selectedNode; + let children = node.children && node.children.map(child => { + let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode); + shouldExpand = shouldExpand || shouldExpandChild; + return newChild; + }); + + return { + node: { + ...node, + expanded: node.expanded || shouldExpand, + children + }, + shouldExpand + }; +}; + +export default (state = {attachmentsTree: {}}, action) => { + switch (action.type) { + case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED: + let currentSoftwareProduct = action.response; + let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {}; + let errorList = createErrorList(attachmentsTree); + return { + ...state, + attachmentsTree, + errorList + }; + case actionTypes.TOGGLE_EXPANDED: + return { + ...state, + attachmentsTree: toggleExpanded(state.attachmentsTree, action.path) + }; + case actionTypes.SELECTED_NODE: + let selectedNode = action.nodeName; + return { + ...state, + attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node, + selectedNode + }; + case actionTypes.UNSELECTED_NODE: + return { + ...state, + selectedNode: undefined + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx new file mode 100644 index 0000000000..7e2dda8d47 --- /dev/null +++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx @@ -0,0 +1,190 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import classNames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {nodeTypes, mouseActions} from './AttachmentsConstants'; + +const typeToIcon = Object.freeze({ + heat: 'building-o', + volume: 'database', + network: 'cloud', + artifact: 'gear', + env: 'server', + other: 'cube' +}); + +const leftPanelWidth = 250; + +class SoftwareProductAttachmentsView extends React.Component { + + static propTypes = { + attachmentsTree: React.PropTypes.object.isRequired + }; + state = { + treeWidth: '400', + }; + + render() { + let {attachmentsTree, errorList = []} = this.props; + + let {treeWidth} = this.state; + return ( +
      +
      +
      +
      +
      +
      + { + attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind])) + } +
      +
      +
      this.onChangeTreeWidth(e)} /> +
      + {errorList.length ? this.renderErrorList(errorList) :
      {attachmentsTree.children ? + i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }
      } +
      +
      +
      +
      +
      + ); + } + + + + renderNode(node, path) { + let isFolder = node.children && node.children.length > 0; + let {onSelectNode} = this.props; + return ( +
      + { +
      this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}> + { + isFolder && +
      this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}> + +
      + } + { + + + + + } + { + + onSelectNode(node.name)} className={this.getTreeTextClassName(node)}> + {node.name} + + } +
      + } + { + isFolder && + +
      + { + node.children.map((child, ind) => this.renderNode(child, [...path, ind])) + } +
      +
      + } +
      + ); + } + + createErrorList(errorList, node, parent) { + if (node.errors) { + node.errors.forEach(error => errorList.push({ + error, + name: node.name, + parentName: parent.name, + type: node.type + })); + } + if (node.children && node.children.length) { + node.children.map((child) => this.createErrorList(errorList, child, node)); + } + } + + renderErrorList(errors) { + let prevError = {}; + let {selectedNode} = this.props; + return errors.map(error => { + let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName; + prevError = error; + + return ( +
      this.selectNode(error.name)} + className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}> + + { + error.hasParent ? + i18n('{type} {name} in {parentName}: ', { + type: nodeTypes[error.type], + name: error.name, + parentName: error.parentName + }) : + i18n('{type} {name}: ', { + type: nodeTypes[error.type], + name: error.name + }) + } + + {error.errorMessage} +
      + ); + }); + } + + selectNode(currentSelectedNode) { + let {onUnselectNode, onSelectNode, selectedNode} = this.props; + if (currentSelectedNode !== selectedNode) { + onSelectNode(currentSelectedNode); + }else{ + onUnselectNode(); + } + + } + + getTreeRowClassName(name) { + let {hoveredNode, selectedNode} = this.props; + return classNames({ + 'tree-node-row': true, + 'tree-node-selected': name === hoveredNode, + 'tree-node-clicked': name === selectedNode + }); + } + + getTreeTextClassName(node) { + let {selectedNode} = this.props; + return classNames({ + 'tree-element-text': true, + 'error-status': node.errors, + 'error-status-selected': node.name === selectedNode + }); + } + + onChangeTreeWidth(e) { + if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { + let onMouseMove = (e) => { + this.setState({treeWidth: e.clientX - leftPanelWidth}); + }; + let onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } + } +} + +export default SoftwareProductAttachmentsView; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js new file mode 100644 index 0000000000..d39b2affd3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js @@ -0,0 +1,188 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import LicenseModelActionHelper from './licenseModel/LicenseModelActionHelper.js'; +import LicenseAgreementActionHelper from './licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupsActionHelper from './licenseModel/featureGroups/FeatureGroupsActionHelper.js'; +import LicenseKeyGroupsActionHelper from './licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; +import EntitlementPoolsActionHelper from './licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; +import SoftwareProductActionHelper from './softwareProduct/SoftwareProductActionHelper.js'; +import SoftwareProductProcessesActionHelper from './softwareProduct/processes/SoftwareProductProcessesActionHelper.js'; +import SoftwareProductNetworksActionHelper from './softwareProduct/networks/SoftwareProductNetworksActionHelper.js'; +import SoftwareProductComponentsActionHelper from './softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductComponentProcessesActionHelper from './softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js'; +import SoftwareProductComponentsNetworkActionHelper from './softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js'; +import SoftwareProductComponentsMonitoringAction from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js'; +import {actionTypes, enums} from './OnboardingConstants.js'; +import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import store from 'sdc-app/AppStore.js'; + +function setCurrentScreen(dispatch, screen, props = {}) { + dispatch({ + type: actionTypes.SET_CURRENT_SCREEN, + currentScreen: { + screen, + props + } + }); +} + +function getCurrentLicenseModelVersion(licenseModelId) { + return store.getState().licenseModelList.find(({id}) => id === licenseModelId).version; +} + +export default { + + navigateToOnboardingCatalog(dispatch) { + LicenseModelActionHelper.fetchLicenseModels(dispatch); + SoftwareProductActionHelper.fetchSoftwareProductList(dispatch); + setCurrentScreen(dispatch, enums.SCREEN.ONBOARDING_CATALOG); + }, + + navigateToLicenseAgreements(dispatch, {licenseModelId, version}) { + if(!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version}); + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_AGREEMENTS, {licenseModelId}); + }); + }, + + navigateToFeatureGroups(dispatch, {licenseModelId, version}) { + if(!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); + setCurrentScreen(dispatch, enums.SCREEN.FEATURE_GROUPS, {licenseModelId}); + }, + + navigateToEntitlementPools(dispatch, {licenseModelId, version}) { + if(!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + setCurrentScreen(dispatch, enums.SCREEN.ENTITLEMENT_POOLS, {licenseModelId}); + }, + + navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}) { + if(!version) { + version = getCurrentLicenseModelVersion(licenseModelId); + } + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); + setCurrentScreen(dispatch, enums.SCREEN.LICENSE_KEY_GROUPS, {licenseModelId}); + }, + + navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version, licensingVersion}) { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}).then(response => { + if(!licensingVersion) { + licensingVersion = response[0].licensingVersion; + } + if (!licenseModelId) { + licenseModelId = response[0].vendorId; + } + + SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}); + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version}); + + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version}); + }); + }, + + navigateToSoftwareProductDetails(dispatch, {softwareProductId}) { + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DETAILS, {softwareProductId}); + }, + + navigateToSoftwareProductAttachments(dispatch, {softwareProductId}) { + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS, {softwareProductId}); + }, + + navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}) { + if (softwareProductId) { + SoftwareProductProcessesActionHelper.fetchProcessesList(dispatch, {softwareProductId, version}); + } + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES, {softwareProductId}); + }, + + navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version}) { + if (softwareProductId) { + SoftwareProductNetworksActionHelper.fetchNetworksList(dispatch, {softwareProductId, version}); + } + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS, {softwareProductId}); + }, + + navigateToSoftwareProductComponents(dispatch, {softwareProductId}) { + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId}); + }, + + navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId, version}) { + if (componentId && softwareProductId) { + SoftwareProductComponentProcessesActionHelper.fetchProcessesList(dispatch, {componentId, softwareProductId, version}); + } + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, {softwareProductId, componentId}); + }, + + navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}){ + if (componentId && softwareProductId) { + SoftwareProductComponentsMonitoringAction.fetchExistingFiles(dispatch, {componentId, softwareProductId}); + } + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING, {softwareProductId, componentId}); + }, + + navigateToComponentStorage(dispatch, {softwareProductId, componentId}) { + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE, {softwareProductId, componentId}); + }, + + navigateToComponentCompute(dispatch, {softwareProductId, componentId}) { + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, componentId}); + }, + + navigateToComponentNetwork(dispatch, {softwareProductId, componentId, version}) { + SoftwareProductComponentsNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version}); + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK, {softwareProductId, componentId}); + }, + + navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version}) { + if (componentId && softwareProductId) { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version}); + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(dispatch, { + softwareProductId, + vspComponentId: componentId, + version + }); + } + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL, {softwareProductId, componentId}); + }, + + navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version}) { + this.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version}); + dispatch({ + type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM, + mapOfExpandedIds: { + [SoftwareProductNavigationItems.COMPONENTS]: true, + [SoftwareProductNavigationItems.COMPONENTS + '|' + componentId]: true + } + }); + }, + + navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}) { + setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, componentId}); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js new file mode 100644 index 0000000000..4772c8d9af --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import {default as OnboardingCatalogView, catalogItemTypes} from './OnboardingCatalogView.jsx'; +import OnboardingActionHelper from './OnboardingActionHelper.js'; +import LicenseModelCreationActionHelper from './licenseModel/creation/LicenseModelCreationActionHelper.js'; +import SoftwareProductCreationActionHelper from './softwareProduct/creation/SoftwareProductCreationActionHelper.js'; + + +const mapStateToProps = ({licenseModelList, softwareProductList, softwareProduct: {softwareProductCreation}, licenseModel: {licenseModelCreation}}) => { + + let modalToShow; + + if(licenseModelCreation.data) { + modalToShow = catalogItemTypes.LICENSE_MODEL; + } else if(softwareProductCreation.showModal && softwareProductCreation.data) { + modalToShow = catalogItemTypes.SOFTWARE_PRODUCT; + } + + return { + licenseModelList, + softwareProductList, + modalToShow + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onSelectLicenseModel({id: licenseModelId}) { + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId}); + }, + onSelectSoftwareProduct(softwareProduct) { + let {id: softwareProductId, vendorId: licenseModelId, licensingVersion} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion}); + }, + onAddSoftwareProductClick: () => SoftwareProductCreationActionHelper.open(dispatch), + onAddLicenseModelClick: () => LicenseModelCreationActionHelper.open(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(OnboardingCatalogView); diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx new file mode 100644 index 0000000000..f2a9db1342 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx @@ -0,0 +1,147 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import objectValues from 'lodash/values.js'; +import LicenseModelCreation from './licenseModel/creation/LicenseModelCreation.js'; +import SoftwareProductCreation from './softwareProduct/creation/SoftwareProductCreation.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import classnames from 'classnames'; +import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; + +export const catalogItemTypes = Object.freeze({ + LICENSE_MODEL: 'license-model', + SOFTWARE_PRODUCT: 'software-product' +}); + +const catalogItemTypeClasses = { + LICENSE_MODEL: 'license-model-type', + SOFTWARE_PRODUCT: 'software-product-type' +}; + +class OnboardingCatalogView extends React.Component { + + constructor(props) { + super(props); + this.state = {searchValue: ''}; + this.handleSearch = this.handleSearch.bind(this); + } + + handleSearch(event){ + this.setState({searchValue: event.target.value}); + } + + static propTypes = { + licenseModelList: React.PropTypes.array, + softwareProductList: React.PropTypes.array, + modalToShow: React.PropTypes.oneOf(objectValues(catalogItemTypes)), + onSelectLicenseModel: React.PropTypes.func.isRequired, + onSelectSoftwareProduct: React.PropTypes.func.isRequired, + onAddLicenseModelClick: React.PropTypes.func.isRequired, + onAddSoftwareProductClick: React.PropTypes.func.isRequired + }; + + getModalDetails() { + const {modalToShow} = this.props; + switch (modalToShow) { + case catalogItemTypes.LICENSE_MODEL: + return { + title: i18n('New License Model'), + element: + }; + case catalogItemTypes.SOFTWARE_PRODUCT: + return { + title: i18n('New Software Product'), + element: + }; + } + } + + render() { + const modalDetails = this.getModalDetails(); + const {licenseModelList, softwareProductList, onSelectLicenseModel, onSelectSoftwareProduct, onAddLicenseModelClick, onAddSoftwareProductClick, modalToShow} = this.props; + + return ( +
      +
      +
      {i18n('Onboarding Catalog')}
      + +
      +
      + +
      +
      +
      + {i18n('ADD')} +
      +
      + onAddLicenseModelClick()}>{i18n('New License Model')}
      +
      + onAddSoftwareProductClick()}>{i18n('New Vendor Software Product')} +
      +
      + {licenseModelList.filter(vlm => vlm.vendorName.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1).map(licenseModel => this.renderTile( + { + ...licenseModel, + name: licenseModel.vendorName + }, + catalogItemTypeClasses.LICENSE_MODEL, + () => onSelectLicenseModel(licenseModel)) + )} + {softwareProductList.filter(vsp => vsp.name.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1).map(softwareProduct => this.renderTile(softwareProduct, + catalogItemTypeClasses.SOFTWARE_PRODUCT, + () => onSelectSoftwareProduct(softwareProduct)) + )} +
      + + + {modalDetails && modalDetails.title} + + + { + modalDetails && modalDetails.element + } + + +
      + ); + + } + + getCatalogItemTypeClassByItemType(catalogItemType) { + switch (catalogItemType) { + case catalogItemTypes.LICENSE_MODEL: + return catalogItemTypeClasses.LICENSE_MODEL; + case catalogItemTypes.SOFTWARE_PRODUCT: + return catalogItemTypeClasses.SOFTWARE_PRODUCT; + } + } + + renderTile(catalogItemData, catalogItemTypeClass, onSelect) { + let {status: itemStatus} = VersionControllerUtils.getCheckOutStatusKindByUserID(catalogItemData.status, catalogItemData.lockingUser); + return ( +
      onSelect()}> +
      +
      +
      +
      +
      +
      +
      {catalogItemData.name}
      +
      V {catalogItemData.version}
      +
      +
      +
      +
      +
      + ); + } +} +export default OnboardingCatalogView; diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js new file mode 100644 index 0000000000..d9c177f606 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + SET_CURRENT_SCREEN: null, + SET_CURRENT_LICENSE_MODEL: null +}); + +export const enums = keyMirror({ + + BREADCRUMS: { + LICENSE_MODEL: 'LICENSE_MODEL', + LICENSE_AGREEMENTS: 'LICENSE_AGREEMENTS', + FEATURE_GROUPS: 'FEATURE_GROUPS', + ENTITLEMENT_POOLS: 'ENTITLEMENT_POOLS', + LICENSE_KEY_GROUPS: 'LICENSE_KEY_GROUPS', + + SOFTWARE_PRODUCT: 'SOFTWARE_PRODUCT', + SOFTWARE_PRODUCT_DETAILS: 'SOFTWARE_PRODUCT_DETAILS', + SOFTWARE_PRODUCT_ATTACHMENTS: 'SOFTWARE_PRODUCT_ATTACHMENTS', + SOFTWARE_PRODUCT_PROCESSES: 'SOFTWARE_PRODUCT_PROCESSES', + SOFTWARE_PRODUCT_NETWORKS: 'SOFTWARE_PRODUCT_NETWORKS', + SOFTWARE_PRODUCT_COMPONENTS: 'SOFTWARE_PRODUCT_COMPONENTS', + SOFTWARE_PRODUCT_COMPONENT_PROCESSES: 'SOFTWARE_PRODUCT_COMPONENT_PROCESSES', + SOFTWARE_PRODUCT_COMPONENT_STORAGE: 'SOFTWARE_PRODUCT_COMPONENT_STORAGE', + SOFTWARE_PRODUCT_COMPONENT_GENERAL: 'SOFTWARE_PRODUCT_COMPONENT_GENERAL', + SOFTWARE_PRODUCT_COMPONENT_COMPUTE: 'SOFTWARE_PRODUCT_COMPONENT_COMPUTE', + SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: 'SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING', + SOFTWARE_PRODUCT_COMPONENT_MONITORING: 'SOFTWARE_PRODUCT_COMPONENT_MONITORING' + }, + + SCREEN: { + ONBOARDING_CATALOG: null, + LICENSE_AGREEMENTS: null, + FEATURE_GROUPS: null, + ENTITLEMENT_POOLS: null, + LICENSE_KEY_GROUPS: null, + + SOFTWARE_PRODUCT_LANDING_PAGE: null, + SOFTWARE_PRODUCT_DETAILS: null, + SOFTWARE_PRODUCT_ATTACHMENTS: null, + SOFTWARE_PRODUCT_PROCESSES: null, + SOFTWARE_PRODUCT_NETWORKS: null, + SOFTWARE_PRODUCT_COMPONENTS: null, + SOFTWARE_PRODUCT_COMPONENT_PROCESSES: null, + SOFTWARE_PRODUCT_COMPONENT_COMPUTE: null, + SOFTWARE_PRODUCT_COMPONENT_STORAGE: null, + SOFTWARE_PRODUCT_COMPONENT_NETWORK: null, + SOFTWARE_PRODUCT_COMPONENT_GENERAL: null, + SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: null, + SOFTWARE_PRODUCT_COMPONENT_MONITORING: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx new file mode 100644 index 0000000000..c4627b11b3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx @@ -0,0 +1,501 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {connect} from 'react-redux'; +import isEqual from 'lodash/isEqual.js'; +import objectValues from 'lodash/values.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Application from 'sdc-app/Application.jsx'; +import store from 'sdc-app/AppStore.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import OnboardingCatalog from './OnboardingCatalog.js'; +import LicenseModel from './licenseModel/LicenseModel.js'; +import LicenseAgreementListEditor from './licenseModel/licenseAgreement/LicenseAgreementListEditor.js'; +import FeatureGroupListEditor from './licenseModel/featureGroups/FeatureGroupListEditor.js'; +import LicenseKeyGroupsListEditor from './licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js'; +import EntitlementPoolsListEditor from './licenseModel/entitlementPools/EntitlementPoolsListEditor.js'; +import SoftwareProduct from './softwareProduct/SoftwareProduct.js'; +import SoftwareProductLandingPage from './softwareProduct/landingPage/SoftwareProductLandingPage.js'; +import SoftwareProductDetails from './softwareProduct/details/SoftwareProductDetails.js'; +import SoftwareProductAttachments from './softwareProduct/attachments/SoftwareProductAttachments.js'; +import SoftwareProductProcesses from './softwareProduct/processes/SoftwareProductProcesses.js'; +import SoftwareProductNetworks from './softwareProduct/networks/SoftwareProductNetworks.js'; +import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponentsList.js'; +import SoftwareProductComponentProcessesList from './softwareProduct/components/processes/SoftwareProductComponentProcessesList.js'; +import SoftwareProductComponentStorage from './softwareProduct/components/storage/SoftwareProductComponentStorage.js'; +import SoftwareProductComponentsNetworkList from './softwareProduct/components/network/SoftwareProductComponentsNetworkList.js'; +import SoftwareProductComponentsGeneral from './softwareProduct/components/general/SoftwareProductComponentsGeneral.js'; +import SoftwareProductComponentsCompute from './softwareProduct/components/compute/SoftwareProductComponentCompute.js'; +import SoftwareProductComponentLoadBalancing from './softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js'; +import SoftwareProductComponentsMonitoring from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js'; +import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +import {enums} from './OnboardingConstants.js'; +import OnboardingActionHelper from './OnboardingActionHelper.js'; + + +class OnboardingView extends React.Component { + static propTypes = { + currentScreen: React.PropTypes.shape({ + screen: React.PropTypes.oneOf(objectValues(enums.SCREEN)).isRequired, + props: React.PropTypes.object.isRequired + }).isRequired + }; + + componentDidMount() { + let element = ReactDOM.findDOMNode(this); + element.addEventListener('click', event => { + if (event.target.tagName === 'A') { + event.preventDefault(); + } + }); + ['wheel', 'mousewheel', 'DOMMouseScroll'].forEach(eventType => + element.addEventListener(eventType, event => event.stopPropagation()) + ); + } + + render() { + let {currentScreen} = this.props; + let {screen, props} = currentScreen; + + return ( +
      + {(() => { + switch (screen) { + case enums.SCREEN.ONBOARDING_CATALOG: + return ; + + case enums.SCREEN.LICENSE_AGREEMENTS: + case enums.SCREEN.FEATURE_GROUPS: + case enums.SCREEN.ENTITLEMENT_POOLS: + case enums.SCREEN.LICENSE_KEY_GROUPS: + return ( + + { + (()=>{ + switch(screen) { + case enums.SCREEN.LICENSE_AGREEMENTS: + return ; + case enums.SCREEN.FEATURE_GROUPS: + return ; + case enums.SCREEN.ENTITLEMENT_POOLS: + return ; + case enums.SCREEN.LICENSE_KEY_GROUPS: + return ; + } + })() + } + + ); + + case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: + case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + return ( + + { + (()=>{ + switch(screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + return ; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + return ; + } + })() + } + + ); + } + })()} +
      + ); + } +} +const mapStateToProps = ({currentScreen}) => ({currentScreen}); +let Onboarding = connect(mapStateToProps, null)(OnboardingView); + +export default class OnboardingPunchOut { + + render({options: {data, apiRoot, apiHeaders}, onEvent}, element) { + if (!this.unsubscribeFromStore) { + this.unsubscribeFromStore = store.subscribe(() => this.handleStoreChange()); + } + + if (!this.isConfigSet) { + Configuration.setATTApiRoot(apiRoot); + Configuration.setATTApiHeaders(apiHeaders); + this.isConfigSet = true; + } + + this.onEvent = (...args) => onEvent(...args); + this.handleData(data); + + if (!this.rendered) { + ReactDOM.render( + + + , + element + ); + this.rendered = true; + } + } + + unmount(element) { + ReactDOM.unmountComponentAtNode(element); + this.rendered = false; + this.unsubscribeFromStore(); + this.unsubscribeFromStore = null; + } + + handleData(data) { + let {breadcrumbs: {selectedKeys = []} = {}} = data; + let dispatch = action => store.dispatch(action); + let {softwareProductList, softwareProduct: {softwareProductEditor: {data: {id: currentSoftwareProductId, version: currentSoftwareProductVersion} = {}}}, + licenseModelList, licenseModel: {licenseModelEditor: {data: {id: currentLicenseModelId, version: currentLicenseModelVersion} = {}}}} = store.getState(); + + if (this.programmaticBreadcrumbsUpdate) { + this.prevSelectedKeys = selectedKeys; + this.programmaticBreadcrumbsUpdate = false; + return; + } + + if (!isEqual(selectedKeys, this.prevSelectedKeys)) { + this.breadcrumbsPrefixSelected = isEqual(selectedKeys, this.prevSelectedKeys && this.prevSelectedKeys.slice(0, selectedKeys.length)); + this.prevSelectedKeys = selectedKeys; + + if (selectedKeys.length === 0) { + OnboardingActionHelper.navigateToOnboardingCatalog(dispatch); + } else if (selectedKeys.length === 1 || selectedKeys[1] === enums.BREADCRUMS.LICENSE_MODEL) { + let [licenseModelId, , licenseModelScreen] = selectedKeys; + if (!licenseModelScreen) { + licenseModelScreen = enums.BREADCRUMS.LICENSE_AGREEMENTS; + } + if(currentLicenseModelId !== licenseModelId) { + currentLicenseModelVersion = licenseModelList.find(lm => lm.id === licenseModelId).version; + } + switch (licenseModelScreen) { + case enums.BREADCRUMS.LICENSE_AGREEMENTS: + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.FEATURE_GROUPS: + OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.ENTITLEMENT_POOLS: + OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + case enums.BREADCRUMS.LICENSE_KEY_GROUPS: + OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion}); + break; + } + } else if (selectedKeys.length <= 4 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT) { + let [licenseModelId, , softwareProductId, softwareProductScreen] = selectedKeys; + let softwareProduct = softwareProductId ? + softwareProductList.find(({id}) => id === softwareProductId) : + softwareProductList.find(({vendorId}) => vendorId === licenseModelId); + if (!softwareProductId) { + softwareProductId = softwareProduct.id; + } + if(currentSoftwareProductId !== softwareProductId) { + currentSoftwareProductVersion = softwareProduct.version; + } + switch (softwareProductScreen) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS: + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES: + OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS: + OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS: + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + dispatch({ + type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM, + mapOfExpandedIds: { + [SoftwareProductNavigationItems.COMPONENTS]: true + } + }); + break; + default: + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: currentSoftwareProductVersion}); + break; + } + }else if (selectedKeys.length === 5 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) { + let [licenseModelId, , softwareProductId, , componentId] = selectedKeys; + let softwareProduct = softwareProductId ? + softwareProductList.find(({id}) => id === softwareProductId) : + softwareProductList.find(({vendorId}) => vendorId === licenseModelId); + if (!softwareProductId) { + softwareProductId = softwareProduct.id; + } + if(currentSoftwareProductId !== softwareProductId) { + currentSoftwareProductVersion = softwareProduct.version; + } + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); + }else if (selectedKeys.length === 6 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) { + let [licenseModelId, , softwareProductId, , componentId, componentScreen] = selectedKeys; + let softwareProduct = softwareProductId ? + softwareProductList.find(({id}) => id === softwareProductId) : + softwareProductList.find(({vendorId}) => vendorId === licenseModelId); + if (!softwareProductId) { + softwareProductId = softwareProduct.id; + } + if(currentSoftwareProductId !== softwareProductId) { + currentSoftwareProductVersion = softwareProduct.version; + } + switch (componentScreen) { + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId}); + break; + case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}); + break; + } + } else { + console.error('Unknown breadcrumbs path: ', selectedKeys); + } + } + } + + handleStoreChange() { + let {currentScreen, licenseModelList, softwareProductList} = store.getState(); + let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList}; + + if (!isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) { + this.prevBreadcrumbsData = breadcrumbsData; + this.breadcrumbsPrefixSelected = false; + this.programmaticBreadcrumbsUpdate = true; + + let breadcrumbs = this.buildBreadcrumbs(breadcrumbsData); + this.onEvent('breadcrumbsupdated', breadcrumbs); + } + } + + buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList}) { + let componentsList; + if(props.componentId) { + componentsList = store.getState().softwareProduct.softwareProductComponents.componentsList; + } + let screenToBreadcrumb; + switch (screen) { + case enums.SCREEN.ONBOARDING_CATALOG: + return []; + + case enums.SCREEN.LICENSE_AGREEMENTS: + case enums.SCREEN.FEATURE_GROUPS: + case enums.SCREEN.ENTITLEMENT_POOLS: + case enums.SCREEN.LICENSE_KEY_GROUPS: + screenToBreadcrumb = { + [enums.SCREEN.LICENSE_AGREEMENTS]: enums.BREADCRUMS.LICENSE_AGREEMENTS, + [enums.SCREEN.FEATURE_GROUPS]: enums.BREADCRUMS.FEATURE_GROUPS, + [enums.SCREEN.ENTITLEMENT_POOLS]: enums.BREADCRUMS.ENTITLEMENT_POOLS, + [enums.SCREEN.LICENSE_KEY_GROUPS]: enums.BREADCRUMS.LICENSE_KEY_GROUPS + }; + return [ + { + selectedKey: props.licenseModelId, + menuItems: licenseModelList.map(({id, vendorName}) => ({ + key: id, + displayText: vendorName + })) + }, + { + selectedKey: enums.BREADCRUMS.LICENSE_MODEL, + menuItems: [{ + key: enums.BREADCRUMS.LICENSE_MODEL, + displayText: i18n('License Model') + }, + ...(softwareProductList.findIndex(({vendorId}) => vendorId === props.licenseModelId) === -1 ? [] : [{ + key: enums.BREADCRUMS.SOFTWARE_PRODUCT, + displayText: i18n('Software Products') + }])] + }, { + selectedKey: screenToBreadcrumb[screen], + menuItems: [{ + key: enums.BREADCRUMS.LICENSE_AGREEMENTS, + displayText: i18n('License Agreements') + }, { + key: enums.BREADCRUMS.FEATURE_GROUPS, + displayText: i18n('Feature Groups') + }, { + key: enums.BREADCRUMS.ENTITLEMENT_POOLS, + displayText: i18n('Entitlement Pools') + }, { + key: enums.BREADCRUMS.LICENSE_KEY_GROUPS, + displayText: i18n('License Key Groups') + }] + } + ]; + + case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: + case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + screenToBreadcrumb = { + [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, + [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS + }; + let componentScreenToBreadcrumb = { + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING + }; + let licenseModelId = softwareProductList.find(({id}) => id === props.softwareProductId).vendorId; + let returnedBreadcrumb = [ + { + selectedKey: licenseModelId, + menuItems: licenseModelList.map(({id, vendorName}) => ({ + key: id, + displayText: vendorName + })) + }, + { + selectedKey: enums.BREADCRUMS.SOFTWARE_PRODUCT, + menuItems: [{ + key: enums.BREADCRUMS.LICENSE_MODEL, + displayText: i18n('License Model') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT, + displayText: i18n('Software Products') + }] + }, + { + selectedKey: props.softwareProductId, + menuItems: softwareProductList + .filter(({vendorId}) => vendorId === licenseModelId) + .map(({id, name}) => ({ + key: id, + displayText: name + })) + }, + ...(screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] : [{ + selectedKey: screenToBreadcrumb[screen] || enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, + menuItems: [{ + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS, + displayText: i18n('General') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS, + displayText: i18n('Attachments') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES, + displayText: i18n('Process Details') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS, + displayText: i18n('Networks') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS, + displayText: i18n('Components') + }] + }]) + ]; + if(props.componentId) { + returnedBreadcrumb = [ + ...returnedBreadcrumb, { + selectedKey: props.componentId, + menuItems: componentsList + .map(({id, displayName}) => ({ + key: id, + displayText: displayName + })) + }, + ...[{ + selectedKey: componentScreenToBreadcrumb[screen], + menuItems: [{ + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL, + displayText: i18n('General') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, + displayText: i18n('Compute') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, + displayText: i18n('High Availability & Load Balancing') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK, + displayText: i18n('Networks') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE, + displayText: i18n('Storage') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, + displayText: i18n('Process Details') + }, { + key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING, + displayText: i18n('Monitoring') + }] + }] + ]; + } + return returnedBreadcrumb; + } + } +} diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js new file mode 100644 index 0000000000..9af2427243 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js @@ -0,0 +1,29 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes, enums} from './OnboardingConstants.js'; + +export const currentScreenReducer = (state = {screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => { + if (action.type === actionTypes.SET_CURRENT_SCREEN) { + return action.currentScreen; + } + return state; +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js new file mode 100644 index 0000000000..92d53a3d4f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {currentScreenReducer} from './OnboardingReducers.js'; +import licenseModelListReducer from './licenseModel/LicenseModelListReducer.js'; +import finalizedLicenseModelListReducer from './licenseModel/FinalizedLicenseModelListReducer.js'; +import licenseModelReducer from './licenseModel/LicenseModelReducer.js'; +import softwareProductReducer from './softwareProduct/SoftwareProductReducer.js'; +import softwareProductListReducer from './softwareProduct/SoftwareProductListReducer.js'; + + +export default { + currentScreen: currentScreenReducer, + licenseModelList: licenseModelListReducer, + finalizedLicenseModelList: finalizedLicenseModelListReducer, + licenseModel: licenseModelReducer, + softwareProduct: softwareProductReducer, + softwareProductList: softwareProductListReducer +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js new file mode 100644 index 0000000000..a851e77dc8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './LicenseModelConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FINALIZED_LICENSE_MODELS_LIST_LOADED: + return [...action.response.results]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js new file mode 100644 index 0000000000..ad91a0da65 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js @@ -0,0 +1,147 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; + +import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; + +import {navigationItems} from './LicenseModelConstants.js'; +import LicenseModelActionHelper from './LicenseModelActionHelper.js'; +import LicenseAgreementActionHelper from './licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupsActionHelper from './featureGroups/FeatureGroupsActionHelper.js'; +import EntitlementPoolsActionHelper from './entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseKeyGroupsActionHelper from './licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; + + +const buildNavigationBarProps = (licenseModel, screen) => { + const {id, vendorName, version} = licenseModel; + const meta = {version}; + + const groups = [{ + id, + name: vendorName, + items: [ + { + id: navigationItems.LICENSE_AGREEMENTS, + name: i18n('License Agreements'), + meta + }, + { + id: navigationItems.FEATURE_GROUPS, + name: i18n('Feature Groups'), + meta + }, + { + id: navigationItems.ENTITLEMENT_POOLS, + name: i18n('Entitlement Pools'), + meta + }, + { + id: navigationItems.LICENSE_KEY_GROUPS, + name: i18n('License Key Groups'), + meta + } + ] + }]; + + const activeItemId = ({ + [enums.SCREEN.LICENSE_AGREEMENTS]: navigationItems.LICENSE_AGREEMENTS, + [enums.SCREEN.FEATURE_GROUPS]: navigationItems.FEATURE_GROUPS, + [enums.SCREEN.ENTITLEMENT_POOLS]: navigationItems.ENTITLEMENT_POOLS, + [enums.SCREEN.LICENSE_KEY_GROUPS]: navigationItems.LICENSE_KEY_GROUPS + })[screen]; + + return { + activeItemId, groups + }; +}; + + +const buildVersionControllerProps = (licenseModel) => { + let {version, viewableVersions, status: currentStatus, lockingUser} = licenseModel; + let {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ? + VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) : + {status: currentStatus, isCheckedOut: false}; + + return { + version, + viewableVersions, + status, + isCheckedOut + }; +}; + + +const mapStateToProps = ({licenseModel: {licenseModelEditor}}, {currentScreen: {screen}}) => { + return { + versionControllerProps: buildVersionControllerProps(licenseModelEditor.data), + navigationBarProps: buildNavigationBarProps(licenseModelEditor.data, screen) + }; +}; + + +const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {licenseModelId}}}) => { + return { + onVersionControllerAction: action => + LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => { + switch(screen) { + case enums.SCREEN.LICENSE_AGREEMENTS: + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId}); + break; + case enums.SCREEN.FEATURE_GROUPS: + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}); + break; + case enums.SCREEN.ENTITLEMENT_POOLS: + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId}); + break; + case enums.SCREEN.LICENSE_KEY_GROUPS: + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId}); + break; + } + }), + onVersionSwitching: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}), + onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch), + + onNavigate: ({id, meta: {version}}) => { + switch(id) { + case navigationItems.LICENSE_AGREEMENTS: + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version}); + break; + case navigationItems.FEATURE_GROUPS: + OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version}); + break; + case navigationItems.ENTITLEMENT_POOLS: + OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version}); + break; + case navigationItems.LICENSE_KEY_GROUPS: + OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}); + break; + } + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(TabulatedEditor); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js new file mode 100644 index 0000000000..a379a2c40f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js @@ -0,0 +1,101 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes} from './LicenseModelConstants.js'; +import {actionsEnum as vcActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/`; +} + +function fetchLicenseModels() { + return RestAPIUtil.fetch(baseUrl()); +} + +function fetchFinalizedLicenseModels() { + return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`); +} + +function fetchLicenseModelById(licenseModelId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl()}${licenseModelId}${versionQuery}`); +} + +function putLicenseModelAction(id, action) { + return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +} + +const LicenseModelActionHelper = { + + fetchLicenseModels(dispatch) { + return fetchLicenseModels().then(response => { + dispatch({ + type: actionTypes.LICENSE_MODELS_LIST_LOADED, + response + }); + }); + }, + + fetchFinalizedLicenseModels(dispatch) { + return fetchFinalizedLicenseModels().then(response => dispatch({ + type: actionTypes.FINALIZED_LICENSE_MODELS_LIST_LOADED, + response + })); + + }, + + fetchLicenseModelById(dispatch, {licenseModelId, version}) { + return fetchLicenseModelById(licenseModelId, version).then(response => { + if(version) { + response.version = version; + } + dispatch({ + type: actionTypes.LICENSE_MODEL_LOADED, + response + }); + }); + }, + + addLicenseModel(dispatch, {licenseModel}){ + dispatch({ + type: actionTypes.ADD_LICENSE_MODEL, + licenseModel + }); + }, + + performVCAction(dispatch, {licenseModelId, action}) { + return putLicenseModelAction(licenseModelId, action).then(() => { + if(action === vcActionsEnum.SUBMIT){ + dispatch({ + type: NotificationConstants.NOTIFY_SUCCESS, + data: {title: i18n('Submit Succeeded'), msg: i18n('This license model successfully submitted'), timeout: 2000} + }); + } + return LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId}); + }); + } +}; + +export default LicenseModelActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js new file mode 100644 index 0000000000..13fa9f5284 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + LICENSE_MODEL_LOADED: null, + LICENSE_MODELS_LIST_LOADED: null, + FINALIZED_LICENSE_MODELS_LIST_LOADED: null, + ADD_LICENSE_MODEL: null, + EDIT_LICENSE_MODEL: null +}); + +export const navigationItems = keyMirror({ + LICENSE_AGREEMENTS: 'License Agreements', + FEATURE_GROUPS: 'Feature Groups', + ENTITLEMENT_POOLS: 'Entitlement Pools', + LICENSE_KEY_GROUPS: 'License Key Groups' +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js new file mode 100644 index 0000000000..e92e32aa9e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './LicenseModelConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.LICENSE_MODEL_LOADED: + return { + ...state, + data: action.response + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js new file mode 100644 index 0000000000..8874c4ce21 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js @@ -0,0 +1,32 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './LicenseModelConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.LICENSE_MODELS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.ADD_LICENSE_MODEL: + return [...state, action.licenseModel]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js new file mode 100644 index 0000000000..5982b9f8ab --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {combineReducers} from 'redux'; + +import licenseModelCreationReducer from './creation/LicenseModelCreationReducer.js'; +import licenseModelEditorReducer from './LicenseModelEditorReducer.js'; + +import licenseAgreementListReducer from './licenseAgreement/LicenseAgreementListReducer.js'; +import licenseAgreementEditorReducer from './licenseAgreement/LicenseAgreementEditorReducer.js'; +import {actionTypes as licenseAgreementActionTypes} from './licenseAgreement/LicenseAgreementConstants.js'; + +import featureGroupsEditorReducer from './featureGroups/FeatureGroupsEditorReducer.js'; +import featureGroupsListReducer from './featureGroups/FeatureGroupsListReducer.js'; +import {actionTypes as featureGroupsActionConstants} from './featureGroups/FeatureGroupsConstants'; + +import entitlementPoolsListReducer from './entitlementPools/EntitlementPoolsListReducer.js'; +import entitlementPoolsEditorReducer from './entitlementPools/EntitlementPoolsEditorReducer.js'; +import {actionTypes as entitlementPoolsConstants} from './entitlementPools/EntitlementPoolsConstants'; + +import licenseKeyGroupsEditorReducer from './licenseKeyGroups/LicenseKeyGroupsEditorReducer.js'; +import licenseKeyGroupsListReducer from './licenseKeyGroups/LicenseKeyGroupsListReducer.js'; +import {actionTypes as licenseKeyGroupsConstants} from './licenseKeyGroups/LicenseKeyGroupsConstants.js'; + +export default combineReducers({ + licenseModelCreation: licenseModelCreationReducer, + licenseModelEditor: licenseModelEditorReducer, + + licenseAgreement: combineReducers({ + licenseAgreementEditor: licenseAgreementEditorReducer, + licenseAgreementList: licenseAgreementListReducer, + licenseAgreementToDelete: (state = false, action) => action.type === licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM ? action.licenseAgreementToDelete : state + }), + featureGroup: combineReducers({ + featureGroupEditor: featureGroupsEditorReducer, + featureGroupsList: featureGroupsListReducer, + featureGroupToDelete: (state = false, action) => action.type === featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM ? action.featureGroupToDelete : state + }), + entitlementPool: combineReducers({ + entitlementPoolEditor: entitlementPoolsEditorReducer, + entitlementPoolsList: entitlementPoolsListReducer, + entitlementPoolToDelete: (state = false, action) => action.type === entitlementPoolsConstants.ENTITLEMENT_POOLS_DELETE_CONFIRM ? action.entitlementPoolToDelete : state + }), + licenseKeyGroup: combineReducers({ + licenseKeyGroupsEditor: licenseKeyGroupsEditorReducer, + licenseKeyGroupsList: licenseKeyGroupsListReducer, + licenseKeyGroupToDelete: (state = false, action) => action.type === licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM ? action.licenseKeyGroupToDelete : state + }), +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js new file mode 100644 index 0000000000..63d0f27b6a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js @@ -0,0 +1,41 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import LicenseModelCreationActionHelper from './LicenseModelCreationActionHelper.js'; +import LicenseModelCreationView from './LicenseModelCreationView.jsx'; + +const mapStateToProps = ({licenseModel: {licenseModelCreation}}) => licenseModelCreation; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => LicenseModelCreationActionHelper.dataChanged(dispatch, {deltaData}), + onCancel: () => LicenseModelCreationActionHelper.close(dispatch), + onSubmit: (licenseModel) => { + LicenseModelCreationActionHelper.close(dispatch); + LicenseModelCreationActionHelper.createLicenseModel(dispatch, {licenseModel}).then(licenseModelId => { + OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId}); + }); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseModelCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js new file mode 100644 index 0000000000..c2a0409bd2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import {actionTypes} from './LicenseModelCreationConstants.js'; + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/`; +} + +function createLicenseModel(licenseModel) { + return RestAPIUtil.create(baseUrl(), { + vendorName: licenseModel.vendorName, + description: licenseModel.description, + iconRef: 'icon' + }); +} + + +export default { + + open(dispatch) { + dispatch({ + type: actionTypes.OPEN + }); + }, + + close(dispatch){ + dispatch({ + type: actionTypes.CLOSE + }); + }, + + dataChanged(dispatch, {deltaData}){ + dispatch({ + type: actionTypes.DATA_CHANGED, + deltaData + }); + }, + + createLicenseModel(dispatch, {licenseModel}){ + return createLicenseModel(licenseModel).then(response => { + LicenseModelActionHelper.addLicenseModel(dispatch, { + licenseModel: { + ...licenseModel, + id: response.value + } + }); + return response.value; + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js new file mode 100644 index 0000000000..603d177048 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + OPEN: null, + CLOSE: null, + DATA_CHANGED: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js new file mode 100644 index 0000000000..a54d1b3089 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './LicenseModelCreationConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.OPEN: + return { + ...state, + data: {} + }; + case actionTypes.CLOSE: + return {}; + case actionTypes.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx new file mode 100644 index 0000000000..4dccc9e1c4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; + +const LicenseModelPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + vendorName: React.PropTypes.string, + description: React.PropTypes.string +}); + +class LicenseModelCreationView extends React.Component { + + static propTypes = { + data: LicenseModelPropType, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {data = {}, onDataChanged} = this.props; + let {vendorName, description} = data; + return ( +
      + this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true}> + onDataChanged({vendorName})} + validations={{maxLength: 25, required: true}} + type='text' + className='field-section'/> + onDataChanged({description})} + validations={{maxLength: 1000, required: true}} + type='textarea' + className='field-section'/> + +
      + ); + } + + + submit() { + const {data:licenseModel} = this.props; + this.props.onSubmit(licenseModel); + } +} + +export default LicenseModelCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js new file mode 100644 index 0000000000..631597a5b0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js @@ -0,0 +1,149 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes as entitlementPoolsActionTypes } from './EntitlementPoolsConstants.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; + +function baseUrl(licenseModelId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/entitlement-pools`; +} + +function fetchEntitlementPoolsList(licenseModelId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +} + +function postEntitlementPool(licenseModelId, entitlementPool) { + return RestAPIUtil.create(baseUrl(licenseModelId), { + name: entitlementPool.name, + description: entitlementPool.description, + thresholdValue: entitlementPool.thresholdValue, + thresholdUnits: entitlementPool.thresholdUnits, + entitlementMetric: entitlementPool.entitlementMetric, + increments: entitlementPool.increments, + aggregationFunction: entitlementPool.aggregationFunction, + operationalScope: entitlementPool.operationalScope, + time: entitlementPool.time, + manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber + }); +} + + +function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool) { + return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${entitlementPool.id}`, { + name: entitlementPool.name, + description: entitlementPool.description, + thresholdValue: entitlementPool.thresholdValue, + thresholdUnits: entitlementPool.thresholdUnits, + entitlementMetric: entitlementPool.entitlementMetric, + increments: entitlementPool.increments, + aggregationFunction: entitlementPool.aggregationFunction, + operationalScope: entitlementPool.operationalScope, + time: entitlementPool.time, + manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber + }); +} + +function deleteEntitlementPool(licenseModelId, entitlementPoolId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${entitlementPoolId}`); +} + + +export default { + fetchEntitlementPoolsList(dispatch, {licenseModelId, version}) { + return fetchEntitlementPoolsList(licenseModelId, version).then(response => dispatch({ + type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_LIST_LOADED, + response + })); + }, + + openEntitlementPoolsEditor(dispatch, {entitlementPool} = {}) { + dispatch({ + type: entitlementPoolsActionTypes.entitlementPoolsEditor.OPEN, + entitlementPool + }); + }, + + deleteEntitlementPool(dispatch, {licenseModelId, entitlementPoolId}) { + return deleteEntitlementPool(licenseModelId, entitlementPoolId).then(() => { + dispatch({ + type: entitlementPoolsActionTypes.DELETE_ENTITLEMENT_POOL, + entitlementPoolId + }); + }); + }, + + entitlementPoolsEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: entitlementPoolsActionTypes.entitlementPoolsEditor.DATA_CHANGED, + deltaData + }); + }, + + closeEntitlementPoolsEditor(dispatch) { + dispatch({ + type: entitlementPoolsActionTypes.entitlementPoolsEditor.CLOSE + }); + }, + + saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}) { + if (previousEntitlementPool) { + return putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool).then(() => { + dispatch({ + type: entitlementPoolsActionTypes.EDIT_ENTITLEMENT_POOL, + entitlementPool + }); + }); + } + else { + return postEntitlementPool(licenseModelId, entitlementPool).then(response => { + dispatch({ + type: entitlementPoolsActionTypes.ADD_ENTITLEMENT_POOL, + entitlementPool: { + ...entitlementPool, + id: response.value + } + }); + }); + } + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_DELETE_CONFIRM, + entitlementPoolToDelete: false + }); + }, + openDeleteEntitlementPoolConfirm(dispatch, {entitlementPool}) { + dispatch({ + type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_DELETE_CONFIRM, + entitlementPoolToDelete: entitlementPool + }); + }, + + switchVersion(dispatch, {licenseModelId, version}) { + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + this.fetchEntitlementPoolsList(dispatch, {licenseModelId, version}); + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx new file mode 100644 index 0000000000..04f038f5f0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx @@ -0,0 +1,51 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function renderMsg(entitlementPoolToDelete) { + let poolName = entitlementPoolToDelete ? entitlementPoolToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{poolName}"?', {poolName}); + let subMsg = entitlementPoolToDelete + && entitlementPoolToDelete.referencingFeatureGroups + && entitlementPoolToDelete.referencingFeatureGroups.length > 0 ? + i18n('This entitlement pool is associated with one or more feature groups') : + ''; + return ( +
      +

      {msg}

      +

      {subMsg}

      +
      + ); +}; + +const mapStateToProps = ({licenseModel: {entitlementPool}}, {licenseModelId}) => { + let {entitlementPoolToDelete} = entitlementPool; + const show = entitlementPoolToDelete !== false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: renderMsg(entitlementPoolToDelete), + confirmationDetails: {entitlementPoolToDelete, licenseModelId} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({entitlementPoolToDelete, licenseModelId}) => { + EntitlementPoolsActionHelper.deleteEntitlementPool(dispatch, { + licenseModelId, + entitlementPoolId: entitlementPoolToDelete.id + }); + EntitlementPoolsActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + EntitlementPoolsActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js new file mode 100644 index 0000000000..8a855076f3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js @@ -0,0 +1,112 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + + ENTITLEMENT_POOLS_LIST_LOADED: null, + ADD_ENTITLEMENT_POOL: null, + EDIT_ENTITLEMENT_POOL: null, + DELETE_ENTITLEMENT_POOL: null, + ENTITLEMENT_POOLS_DELETE_CONFIRM: null, + + entitlementPoolsEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + } + +}); + +export const enums = keyMirror({ + SELECTED_FEATURE_GROUP_TAB: { + GENERAL: 1, + ENTITLEMENT_POOLS: 2, + LICENCE_KEY_GROUPS: 3 + }, + SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: { + ASSOCIATED_ENTITLEMENT_POOLS: 1, + AVAILABLE_ENTITLEMENT_POOLS: 2 + } +}); + +export const defaultState = { + ENTITLEMENT_POOLS_EDITOR_DATA: { + entitlementMetric: {choice: '', other: ''}, + aggregationFunction: {choice: '', other: ''}, + operationalScope: {choices: [], other: ''}, + time: {choice: '', other: ''} + } +}; + +export const thresholdUnitType = { + ABSOLUTE: 'Absolute', + PERCENTAGE: 'Percentage' +}; + +export const optionsInputValues = { + OPERATIONAL_SCOPE: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Network_Wide', title: 'Network Wide'}, + {enum: 'Availability_Zone', title: 'Availability Zone'}, + {enum: 'Data_Center', title: 'Data Center'}, + {enum: 'Tenant', title: 'Tenant'}, + {enum: 'VM', title: 'VM'}, + {enum: 'CPU', title: 'CPU'}, + {enum: 'Core', title: 'Core'} + ], + TIME: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Hour', title: 'Hour'}, + {enum: 'Day', title: 'Day'}, + {enum: 'Month', title: 'Month'} + ], + AGGREGATE_FUNCTION: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Peak', title: 'Peak'}, + {enum: 'Average', title: 'Average'} + ], + ENTITLEMENT_METRIC: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Software_Instances_Count', title: 'Software Instances'}, + {enum: 'Core', title: 'Core'}, + {enum: 'CPU', title: 'CPU'}, + {enum: 'Trunks', title: 'Trunks'}, + {enum: 'User', title: 'User'}, + {enum: 'Subscribers', title: 'Subscribers'}, + {enum: 'Tenants', title: 'Tenants'}, + {enum: 'Tokens', title: 'Tokens'}, + {enum: 'Seats', title: 'Seats'}, + {enum: 'Units_TB', title: 'Units-TB'}, + {enum: 'Units_GB', title: 'Units-GB'}, + {enum: 'Units_MB', title: 'Units-MB'} + ], + THRESHOLD_UNITS: [ + {enum: '', title: i18n('please select…')}, + {enum: thresholdUnitType.ABSOLUTE, title: 'Absolute'}, + {enum: thresholdUnitType.PERCENTAGE, title: '%'} + ] +}; + + + + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js new file mode 100644 index 0000000000..d5bd07e929 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; +import EntitlementPoolsEditorView from './EntitlementPoolsEditorView.jsx'; + +const mapStateToProps = ({licenseModel: {entitlementPool}}) => { + + + let {data} = entitlementPool.entitlementPoolEditor; + + let previousData; + const entitlementPoolId = data ? data.id : null; + if(entitlementPoolId) { + previousData = entitlementPool.entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId); + } + + return { + data, + previousData + }; +}; + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onDataChanged: deltaData => EntitlementPoolsActionHelper.entitlementPoolsEditorDataChanged(dispatch, {deltaData}), + onCancel: () => EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch), + onSubmit: ({previousEntitlementPool, entitlementPool}) => { + EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch); + EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(EntitlementPoolsEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js new file mode 100644 index 0000000000..86e97ecf8d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes, defaultState} from './EntitlementPoolsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.entitlementPoolsEditor.OPEN: + return { + ...state, + data: action.entitlementPool ? {...action.entitlementPool} : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA + }; + case actionTypes.entitlementPoolsEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.entitlementPoolsEditor.CLOSE: + return {}; + 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 new file mode 100644 index 0000000000..77c5a12e03 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx @@ -0,0 +1,167 @@ +import React from 'react'; + + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType} from './EntitlementPoolsConstants.js'; +import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; + + +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 + }), + 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 + }) +}); + +class EntitlementPoolsEditorView extends React.Component { + + static propTypes = { + data: EntitlementPoolPropType, + previousData: EntitlementPoolPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + static defaultProps = { + data: {} + }; + + render() { + let {data = {}, onDataChanged, isReadOnlyMode} = this.props; + let { + name, description, manufacturerReferenceNumber, operationalScope, aggregationFunction, thresholdUnits, thresholdValue, + increments, time, entitlementMetric} = data; + let thresholdValueValidation = thresholdUnits === thresholdUnitType.PERCENTAGE ? {numeric: true, required: true, maxValue: 100} : {numeric: true, required: true}; + let timeValidation = time && time.choice === optionInputOther.OTHER ? {numeric: true, required: true} : {required: true}; + + return ( + this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + className='entitlement-pools-form'> +
      + onDataChanged({name})} + label={i18n('Name')} + value={name} + validations={{maxLength: 120, required: true}} + type='text'/> + + onDataChanged({operationalScope:{choices: operationalScope, other: ''}})} + onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], other: operationalScope}})} + multiSelectedEnum={operationalScope && operationalScope.choices} + label={i18n('Operational Scope')} + otherValue={operationalScope && operationalScope.other} + validations={{required: true}} + values={EntitlementPoolsOptionsInputValues.OPERATIONAL_SCOPE}/> + +
      +
      + onDataChanged({description})} + label={i18n('Description')} + value={description} + validations={{maxLength: 1000, required: true}} + type='textarea'/> +
      +
      + onDataChanged({thresholdUnits})} + selectedEnum={thresholdUnits} + label={i18n('Threshold Value')} + type='select' + values={EntitlementPoolsOptionsInputValues.THRESHOLD_UNITS} + validations={{required: true}}/> + onDataChanged({thresholdValue})} + value={thresholdValue} + validations={thresholdValueValidation} + type='text'/> +
      + + onDataChanged({entitlementMetric:{choice: entitlementMetric, other: ''}})} + onOtherChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: optionInputOther.OTHER, other: entitlementMetric}})} + selectedEnum={entitlementMetric && entitlementMetric.choice} + otherValue={entitlementMetric && entitlementMetric.other} + label={i18n('Entitlement Metric')} + validations={{required: true}} + values={EntitlementPoolsOptionsInputValues.ENTITLEMENT_METRIC}/> + onDataChanged({aggregationFunction:{choice: aggregationFunction, other: ''}})} + onOtherChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: optionInputOther.OTHER, other: aggregationFunction}})} + selectedEnum={aggregationFunction && aggregationFunction.choice} + otherValue={aggregationFunction && aggregationFunction.other} + validations={{required: true}} + label={i18n('Aggregate Function')} + values={EntitlementPoolsOptionsInputValues.AGGREGATE_FUNCTION}/> + +
      +
      +
      + + onDataChanged({manufacturerReferenceNumber})} + label={i18n('Manufacturer Reference Number')} + value={manufacturerReferenceNumber} + validations={{maxLength: 100, required: true}} + type='text'/> + + onDataChanged({time:{choice: time, other: ''}})} + onOtherChange={time => onDataChanged({time:{choice: optionInputOther.OTHER, other: time}})} + selectedEnum={time && time.choice} + otherValue={time && time.other} + validations={timeValidation} + label={i18n('Time')} + values={EntitlementPoolsOptionsInputValues.TIME}/> +
      +
      + onDataChanged({increments})} + label={i18n('Increments')} + value={increments} + validations={{maxLength: 120}} + type='text'/> + +
      +
      + ); + } + + submit() { + const {data: entitlementPool, previousData: previousEntitlementPool} = this.props; + this.props.onSubmit({entitlementPool, previousEntitlementPool}); + } +} + +export default EntitlementPoolsEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js new file mode 100644 index 0000000000..4b21a2fea8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js'; +import EntitlementPoolsListEditorView from './EntitlementPoolsListEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) => { + let {entitlementPoolsList} = entitlementPool; + let {data} = entitlementPool.entitlementPoolEditor; + + let {vendorName} = licenseModelEditor.data; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + return { + vendorName, + entitlementPoolsList, + isReadOnlyMode, + isDisplayModal: Boolean(data), + isModalInEditMode: Boolean(data && data.id), + }; +}; + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onAddEntitlementPoolClick: () => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch), + onEditEntitlementPoolClick: entitlementPool => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch, {entitlementPool}), + onDeleteEntitlementPool: entitlementPool => EntitlementPoolsActionHelper.openDeleteEntitlementPoolConfirm(dispatch, { + licenseModelId, + entitlementPool + }) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(EntitlementPoolsListEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx new file mode 100644 index 0000000000..52df102503 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx @@ -0,0 +1,132 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import EntitlementPoolsEditor from './EntitlementPoolsEditor.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import {optionsInputValues} from './EntitlementPoolsConstants'; +import EntitlementPoolsConfirmationModal from './EntitlementPoolsConfirmationModal.jsx'; + + +class EntitlementPoolsListEditorView extends React.Component { + static propTypes = { + vendorName: React.PropTypes.string, + licenseModelId: React.PropTypes.string.isRequired, + entitlementPoolsList: React.PropTypes.array, + isReadOnlyMode: React.PropTypes.bool.isRequired, + isDisplayModal: React.PropTypes.bool, + isModalInEditMode: React.PropTypes.bool, + onAddEntitlementPoolClick: React.PropTypes.func, + onEditEntitlementPoolClick: React.PropTypes.func, + onDeleteEntitlementPool: React.PropTypes.func, + }; + + static defaultProps = { + entitlementPoolsList: [] + }; + + state = { + localFilter: '' + }; + + render() { + let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {onAddEntitlementPoolClick} = this.props; + const {localFilter} = this.state; + + return ( +
      + this.setState({localFilter: filter})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(entitlementPool => this.renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode))} + + + + {`${isModalInEditMode ? i18n('Edit Entitlement Pool') : i18n('Create New Entitlement Pool')}`} + + + { + isDisplayModal && ( + + ) + } + + + + +
      + ); + } + + filterList() { + let {entitlementPoolsList} = this.props; + let {localFilter} = this.state; + if(localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return entitlementPoolsList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return entitlementPoolsList; + } + } + + renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode) { + let {id, name, description, thresholdValue, thresholdUnits, entitlementMetric, aggregationFunction, + manufacturerReferenceNumber, time} = entitlementPool; + let {onEditEntitlementPoolClick, onDeleteEntitlementPool} = this.props; + return ( + onEditEntitlementPoolClick(entitlementPool)} + onDelete={() => onDeleteEntitlementPool(entitlementPool)} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode}> +
      +
      {i18n('Name')}
      +
      {name}
      +
      + +
      +
      {i18n('Entitlement')}
      +
      {`${this.extractValue(aggregationFunction)} ${this.extractValue(entitlementMetric)} per ${this.extractValue(time)}`}
      +
      {`${thresholdValue ? thresholdValue : ''} ${this.extractUnits(thresholdUnits)}`}
      +
      + +
      +
      {i18n('Manufacturer Reference Number')}
      +
      {manufacturerReferenceNumber}
      +
      + +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      + ); + } + + + + extractUnits(units) { + if (units === undefined) {return '';} //TODO fix it later + return units === 'Absolute' ? '' : '%'; + } + + extractValue(item) { + if (item === undefined) {return '';} //TODO fix it later + + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; + } +} + +export default EntitlementPoolsListEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js new file mode 100644 index 0000000000..63e351fce7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './EntitlementPoolsConstants'; +export default (state = [], action) => { + switch (action.type) { + case actionTypes.ENTITLEMENT_POOLS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.ADD_ENTITLEMENT_POOL: + return [...state, action.entitlementPool]; + case actionTypes.EDIT_ENTITLEMENT_POOL: + const indexForEdit = state.findIndex(entitlementPool => entitlementPool.id === action.entitlementPool.id); + return [...state.slice(0, indexForEdit), action.entitlementPool, ...state.slice(indexForEdit + 1)]; + case actionTypes.DELETE_ENTITLEMENT_POOL: + return state.filter(entitlementPool => entitlementPool.id !== action.entitlementPoolId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js new file mode 100644 index 0000000000..c2b269bcf9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; +import FeatureGroupEditorView from './FeatureGroupEditorView.jsx'; + +const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => { + + const {featureGroupEditor} = featureGroup; + + let {data, selectedTab, selectedEntitlementPoolsButtonTab, selectedLicenseKeyGroupsButtonTab} = featureGroupEditor; + + let previousData; + const featureGroupId = data ? data.id : null; + if (featureGroupId) { + previousData = featureGroup.featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId); + } + let {entitlementPoolsList = []} = entitlementPool; + let {licenseKeyGroupsList = []} = licenseKeyGroup; + + return { + data, + previousData, + selectedTab, + selectedEntitlementPoolsButtonTab, + selectedLicenseKeyGroupsButtonTab, + entitlementPoolsList, + licenseKeyGroupsList + }; +}; + + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onTabSelect: tab => FeatureGroupsActionHelper.selectEntitlementPoolsEditorTab(dispatch, {tab}), + onEntitlementPoolsButtonTabSelect: buttonTab => FeatureGroupsActionHelper.selectFeatureGroupsEditorEntitlementPoolsButtonTab(dispatch, {buttonTab}), + onLicenseKeyGroupsButtonTabSelect: buttonTab => FeatureGroupsActionHelper.selectFeatureGroupsEditorLicenseKeyGroupsButtonTab(dispatch, {buttonTab}), + onDataChanged: deltaData => FeatureGroupsActionHelper.featureGroupsEditorDataChanged(dispatch, {deltaData}), + onSubmit: (previousFeatureGroup, featureGroup) => { + FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch); + FeatureGroupsActionHelper.saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(FeatureGroupEditorView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx new file mode 100644 index 0000000000..6fecd16b71 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx @@ -0,0 +1,339 @@ +import React from 'react'; +import ValidationTabs from 'nfvo-components/input/validation/ValidationTabs.jsx'; +import ValidationTab from 'nfvo-components/input/validation/ValidationTab.jsx'; +import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; +import Button from 'react-bootstrap/lib/Button.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorViewItem from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import {state as FeatureGroupStateConstants} from './FeatureGroupsConstants.js'; + +const FeatureGroupsPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + partNumber: React.PropTypes.string, + entitlementPoolsIds: React.PropTypes.array(React.PropTypes.string), + licenseKeyGroupsIds: React.PropTypes.array(React.PropTypes.string) +}); + +class FeatureGroupEditorView extends React.Component { + + + static propTypes = { + data: FeatureGroupsPropType, + previousData: FeatureGroupsPropType, + isReadOnlyMode: React.PropTypes.bool, + + onSubmit: React.PropTypes.func, + onCancel: React.PropTypes.func, + + selectedTab: React.PropTypes.number, + onTabSelect: React.PropTypes.func, + + selectedEntitlementPoolsButtonTab: React.PropTypes.number, + selectedLicenseKeyGroupsButtonTab: React.PropTypes.number, + onEntitlementPoolsButtonTabSelect: React.PropTypes.func, + onLicenseKeyGroupsButtonTabSelect: React.PropTypes.func, + + entitlementPoolsList: DualListboxView.propTypes.availableList, + licenseKeyGroupsList: DualListboxView.propTypes.availableList + }; + + + static defaultProps = { + data: {}, + selectedTab: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL, + selectedEntitlementPoolsButtonTab: FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS, + selectedLicenseKeyGroupsButtonTab: FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS + }; + + state = { + localEntitlementPoolsListFilter: '', + localLicenseKeyGroupsListFilter: '' + }; + + + render() { + let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + return ( + this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + name='feature-group-validation-form' + className='feature-group-form'> + + {this.renderGeneralTab()} + {this.renderEntitlementPoolsTab()} + {this.renderLicenseKeyGroupsTab()} + + + + ); + } + + submit() { + const {data: featureGroup, previousData: previousFeatureGroup} = this.props; + this.props.onSubmit(previousFeatureGroup, featureGroup); + } + + renderGeneralTab() { + let {data = {}, onDataChanged} = this.props; + let {name, description, partNumber} = data; + return ( + +
      + onDataChanged({name})} + ref='name' + label={i18n('Name')} + value={name} + name='feature-group-name' + validations={{maxLength: 120, required: true}} + type='text'/> + onDataChanged({description})} + ref='description' + label={i18n('Description')} + value={description} + name='feature-group-description' + validations={{maxLength: 1000, required: true}} + type='textarea'/> + onDataChanged({partNumber})} + label={i18n('Part Number')} + value={partNumber} + validations={{required: true}} + type='text'/> +
      +
      + ); + } + + renderEntitlementPoolsTab() { + let {selectedEntitlementPoolsButtonTab, onEntitlementPoolsButtonTabSelect, entitlementPoolsList} = this.props; + if (entitlementPoolsList.length > 0) { + return ( + + + { + this.renderButtonsTab( + FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS, + selectedEntitlementPoolsButtonTab, + i18n('Associated Entitlement Pools'), + onEntitlementPoolsButtonTabSelect + ) + } + { + this.renderButtonsTab( + FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.AVAILABLE_ENTITLEMENT_POOLS, + selectedEntitlementPoolsButtonTab, + i18n('Available Entitlement Pools'), + onEntitlementPoolsButtonTabSelect + ) + } + + {this.renderEntitlementPoolsButtonTabContent(selectedEntitlementPoolsButtonTab)} + + ); + } else { + return ( + +

      {i18n('There is no available entitlement pools.')}

      +
      + ); + } + } + + renderLicenseKeyGroupsTab() { + let {selectedLicenseKeyGroupsButtonTab, onLicenseKeyGroupsButtonTabSelect, licenseKeyGroupsList} = this.props; + if (licenseKeyGroupsList.length > 0) { + return ( + + + { + this.renderButtonsTab( + FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS, + selectedLicenseKeyGroupsButtonTab, + i18n('Associated License Key Groups'), + onLicenseKeyGroupsButtonTabSelect + ) + } + { + this.renderButtonsTab( + FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.AVAILABLE_LICENSE_KEY_GROUPS, + selectedLicenseKeyGroupsButtonTab, + i18n('Available License Key Groups'), + onLicenseKeyGroupsButtonTabSelect + ) + } + + {this.renderLicenseKeyGroupsTabContent(selectedLicenseKeyGroupsButtonTab)} + + ); + } else { + return ( + +

      {i18n('There is no available license key groups')}

      +
      ); + } + } + + renderButtonsTab(buttonTab, selectedButtonTab, title, onClick) { + const isSelected = buttonTab === selectedButtonTab; + return ( + + ); + } + + renderEntitlementPoolsButtonTabContent(selectedFeatureGroupsButtonTab) { + const {entitlementPoolsList = [], data: {entitlementPoolsIds = []}} = this.props; + let dualBoxTitle = { + left: i18n('Available Entitlement Pools'), + right: i18n('Selected Entitlement Pools') + }; + + if (entitlementPoolsList.length) { + const {localEntitlementPoolsListFilter} = this.state; + let selectedEntitlementPools = entitlementPoolsIds.map(entitlementPoolId => entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId)); + + switch (selectedFeatureGroupsButtonTab) { + case FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS: + if (selectedEntitlementPools.length) { + return ( + this.setState({localEntitlementPoolsListFilter})}> + {this.filterAssociatedItems(selectedEntitlementPools, localEntitlementPoolsListFilter) + .map(entitlementPool => this.renderAssociatedListItem(entitlementPool + , FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS))} + + ); + } + else { + return ( +
      +
      {i18n('There are currently no entitlement pools associated with this feature group. Click "Available Entitlement Pools" to associate.')} +
      + ); + } + case FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.AVAILABLE_ENTITLEMENT_POOLS: + return ( + this.props.onDataChanged( { entitlementPoolsIds: selectedValuesList } )}/> + ); + } + } + } + + renderLicenseKeyGroupsTabContent(selectedFeatureGroupsButtonTab) { + const {licenseKeyGroupsList = [], data: {licenseKeyGroupsIds = []}} = this.props; + let dualBoxFilterTitle = { + left: i18n('Available License Key Groups'), + right: i18n('Selected License Key Groups') + }; + + if (licenseKeyGroupsList.length) { + const {localLicenseKeyGroupsListFilter} = this.state; + let selectedLicenseKeyGroups = licenseKeyGroupsIds.map(licenseKeyGroupId => licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId)); + + switch (selectedFeatureGroupsButtonTab) { + case FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS: + if (selectedLicenseKeyGroups.length) { + return ( + this.setState({localLicenseKeyGroupsListFilter})}> + {this.filterAssociatedItems(selectedLicenseKeyGroups, localLicenseKeyGroupsListFilter) + .map(licenseKeyGroup => this.renderAssociatedListItem(licenseKeyGroup + , FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS))} + + ); + } else { + return ( +
      + {i18n('There are currently no license key groups associated with this feature group. Click "Available License Key Groups" to associate.')} +
      + ); + } + case FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.AVAILABLE_LICENSE_KEY_GROUPS: + return ( + this.props.onDataChanged( { licenseKeyGroupsIds: selectedValuesList } )}/> + ); + } + } + } + + + renderAssociatedListItem(listItem, itemType) { + let {isReadOnlyMode} = this.props; + return ( + this.deleteAssociatedItem(listItem.id, itemType)} + isReadOnlyMode={isReadOnlyMode}> +
      {listItem.name}
      +
      {listItem.description}
      +
      + ); + } + + filterAssociatedItems(list, localList) { + if (localList) { + const filter = new RegExp(escape(localList), 'i'); + return list.filter(({name = '', description = ''}) => name.match(filter) || description.match(filter)); + } + else { + return list; + } + } + + deleteAssociatedItem(id, type) { + const {data: {licenseKeyGroupsIds = [], entitlementPoolsIds = []}} = this.props; + if (type === FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS) { + this.props.onDataChanged({licenseKeyGroupsIds: licenseKeyGroupsIds.filter(listId => listId !== id)}); + } else { + this.props.onDataChanged({entitlementPoolsIds: entitlementPoolsIds.filter(listId => listId !== id)}); + } + + } +} + + +export default FeatureGroupEditorView; + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js new file mode 100644 index 0000000000..9ea5a31490 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; +import FeatureGroupListEditorView from './FeatureGroupListEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => { + const {featureGroupEditor: {data}, featureGroupsList} = featureGroup; + let {vendorName} = licenseModelEditor.data; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + return { + vendorName, + featureGroupsModal: { + show: Boolean(data), + editMode: Boolean(data && data.id) + }, + featureGroupsList, + isReadOnlyMode + }; +}; + + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onDeleteFeatureGroupClick: (featureGroup) => FeatureGroupsActionHelper.openDeleteFeatureGroupConfirm(dispatch, {licenseModelId, featureGroup}), + onCancelFeatureGroupsEditor: () => FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch), + + onAddFeatureGroupClick: () => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId}), + onEditFeatureGroupClick: featureGroup => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, { + featureGroup, + licenseModelId + }) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(FeatureGroupListEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx new file mode 100644 index 0000000000..d998f9216f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx @@ -0,0 +1,136 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import FeatureGroupEditor from './FeatureGroupEditor.js'; +import FeatureGroupsConfirmationModal from './FeatureGroupsConfirmationModal.jsx'; + +class FeatureGroupListEditorView extends React.Component { + static propTypes = { + vendorName: React.PropTypes.string, + licenseModelId: React.PropTypes.string.isRequired, + featureGroupsModal: React.PropTypes.shape({ + show: React.PropTypes.bool, + editMode: React.PropTypes.bool + }), + isReadOnlyMode: React.PropTypes.bool.isRequired, + onAddFeatureGroupClick: React.PropTypes.func, + onEditFeatureGroupClick: React.PropTypes.func, + onDeleteFeatureGroupClick: React.PropTypes.func, + onCancelFeatureGroupsEditor: React.PropTypes.func, + featureGroupsList: React.PropTypes.array + }; + + static defaultProps = { + featureGroupsList: [], + featureGroupsModal: { + show: false, + editMode: false + } + }; + + state = { + localFilter: '' + }; + + render() { + let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick} = this.props; + const {localFilter} = this.state; + + return ( +
      + this.setState({localFilter: filter})} + onAdd={() => onAddFeatureGroupClick()} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode))} + + + + {`${featureGroupsModal.editMode ? i18n('Edit Feature Group') : i18n('Create New Feature Group')}`} + + + this.closeFeatureGroupsEditor()} + licenseModelId={licenseModelId} + isReadOnlyMode={isReadOnlyMode}/> + + + + + +
      + ); + } + + + renderFeatureGroupListItem(listItem, isReadOnlyMode) { + let {name, description, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem; + return ( + this.deleteFeatureGroupItem(listItem)} + onSelect={() => this.editFeatureGroupItem(listItem)} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode}> +
      +
      {i18n('Name')}
      +
      {name}
      +
      + +
      +
      +
      {i18n('Entitlement')}
      +
      {i18n('Pools')}
      +
      {entitlementPoolsIds.length || 0}
      +
      +
      +
      {i18n('License key')}
      +
      {i18n('Groups')}
      +
      {licenseKeyGroupsIds.length || 0}
      +
      +
      + +
      +
      {i18n('Description')}
      +
      {description}
      +
      + +
      + ); + } + + filterList() { + let {featureGroupsList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return featureGroupsList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return featureGroupsList; + } + } + + closeFeatureGroupsEditor() { + this.props.onCancelFeatureGroupsEditor(); + } + + editFeatureGroupItem(featureGroup) { + this.props.onEditFeatureGroupClick(featureGroup); + } + + deleteFeatureGroupItem(featureGroup) { + this.props.onDeleteFeatureGroupClick(featureGroup); + } +} + +export default FeatureGroupListEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js new file mode 100644 index 0000000000..3776c01263 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js @@ -0,0 +1,165 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes as featureGroupsActionConstants} from './FeatureGroupsConstants.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; +import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; + +function baseUrl(licenseModelId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/feature-groups`; +} + +function fetchFeatureGroupsList(licenseModelId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +} + +function deleteFeatureGroup(licenseModelId, featureGroupId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${featureGroupId}`); +} + +function addFeatureGroup(licenseModelId, featureGroup) { + return RestAPIUtil.create(baseUrl(licenseModelId), { + name: featureGroup.name, + description: featureGroup.description, + partNumber: featureGroup.partNumber, + addedLicenseKeyGroupsIds: featureGroup.licenseKeyGroupsIds, + addedEntitlementPoolsIds: featureGroup.entitlementPoolsIds + }); +} + +function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup) { + + const {licenseKeyGroupsIds = []} = featureGroup; + const {licenseKeyGroupsIds: prevLicenseKeyGroupsIds = []} = previousFeatureGroup; + const {entitlementPoolsIds = []} = featureGroup; + const {entitlementPoolsIds: prevEntitlementPoolsIds = []} = previousFeatureGroup; + return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${featureGroup.id}`, { + name: featureGroup.name, + description: featureGroup.description, + partNumber: featureGroup.partNumber, + addedLicenseKeyGroupsIds: licenseKeyGroupsIds.filter(licenseKeyGroupId => prevLicenseKeyGroupsIds.indexOf(licenseKeyGroupId) === -1), + removedLicenseKeyGroupsIds: prevLicenseKeyGroupsIds.filter(prevLicenseKeyGroupId => licenseKeyGroupsIds.indexOf(prevLicenseKeyGroupId) === -1), + addedEntitlementPoolsIds: entitlementPoolsIds.filter(entitlementPoolId => prevEntitlementPoolsIds.indexOf(entitlementPoolId) === -1), + removedEntitlementPoolsIds: prevEntitlementPoolsIds.filter(prevEntitlementPoolId => entitlementPoolsIds.indexOf(prevEntitlementPoolId) === -1) + + }); +} + +export default { + fetchFeatureGroupsList(dispatch, {licenseModelId, version}) { + return fetchFeatureGroupsList(licenseModelId, version).then(response => dispatch({ + type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED, + response + })); + }, + + deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId}) { + return deleteFeatureGroup(licenseModelId, featureGroupId).then(() => dispatch({ + type: featureGroupsActionConstants.DELETE_FEATURE_GROUPS, + featureGroupId + })); + }, + + saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}) { + if (previousFeatureGroup) { + return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup).then(() => dispatch({ + type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS, + featureGroup + })); + } + else { + return addFeatureGroup(licenseModelId, featureGroup).then(response => dispatch({ + type: featureGroupsActionConstants.ADD_FEATURE_GROUPS, + featureGroup: { + ...featureGroup, + id: response.value + } + })); + } + }, + + selectEntitlementPoolsEditorTab(dispatch, {tab}) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.SELECT_TAB, + tab + }); + }, + + selectFeatureGroupsEditorEntitlementPoolsButtonTab(dispatch, {buttonTab}) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB, + buttonTab + }); + }, + + selectFeatureGroupsEditorLicenseKeyGroupsButtonTab(dispatch, {buttonTab}) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB, + buttonTab + }); + }, + + openFeatureGroupsEditor(dispatch, {featureGroup, licenseModelId}) { + EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId}); + LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId}); + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.OPEN, + featureGroup + }); + }, + + closeFeatureGroupsEditor(dispatch) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.CLOSE + }); + }, + + featureGroupsEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: featureGroupsActionConstants.featureGroupsEditor.DATA_CHANGED, + deltaData + }); + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM, + featureGroupToDelete: false + }); + }, + + openDeleteFeatureGroupConfirm(dispatch, {featureGroup}) { + dispatch({ + type: featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM, + featureGroupToDelete: featureGroup + }); + }, + + switchVersion(dispatch, {licenseModelId, version}) { + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + this.fetchFeatureGroupsList(dispatch, {licenseModelId, version}); + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx new file mode 100644 index 0000000000..142ec3c4c8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function renderMsg(featureGroupToDelete) { + let name = featureGroupToDelete ? featureGroupToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let subMsg = featureGroupToDelete + && featureGroupToDelete.referencingLicenseAgreements + && featureGroupToDelete.referencingLicenseAgreements.length > 0 ? + i18n('This feature group is associated with one ore more license agreements') : + ''; + return ( +
      +

      {msg}

      +

      {subMsg}

      +
      + ); +}; + +const mapStateToProps = ({licenseModel: {featureGroup}}, {licenseModelId}) => { + let {featureGroupToDelete} = featureGroup; + const show = featureGroupToDelete !== false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: renderMsg(featureGroupToDelete), + confirmationDetails: {featureGroupToDelete, licenseModelId} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({featureGroupToDelete, licenseModelId}) => { + FeatureGroupsActionHelper.deleteFeatureGroup(dispatch, {featureGroupId: featureGroupToDelete.id, licenseModelId}); + FeatureGroupsActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + FeatureGroupsActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js new file mode 100644 index 0000000000..e02c54595d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js @@ -0,0 +1,60 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + FEATURE_GROUPS_LIST_LOADED: null, + ADD_FEATURE_GROUPS: null, + EDIT_FEATURE_GROUPS: null, + DELETE_FEATURE_GROUPS: null, + FEATURE_GROUPS_DELETE_CONFIRM: null, + + + ENTITLEMENT_POOLS_LIST_LOADED: null, + + featureGroupsEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + SELECT_TAB: null, + SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: null, + SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: null + } +}); + +export const state = keyMirror({ + SELECTED_FEATURE_GROUP_TAB: { + GENERAL: 1, + ENTITLEMENT_POOLS: 2, + LICENCE_KEY_GROUPS: 3 + }, + SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: { + ASSOCIATED_ENTITLEMENT_POOLS: 1, + AVAILABLE_ENTITLEMENT_POOLS: 2 + }, + SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: { + ASSOCIATED_LICENSE_KEY_GROUPS: 1, + AVAILABLE_LICENSE_KEY_GROUPS: 2 + }, +}); + + + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js new file mode 100644 index 0000000000..576a5358e6 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './FeatureGroupsConstants.js'; + + + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.featureGroupsEditor.OPEN: + return { + ...state, + data: action.featureGroup || {} + }; + case actionTypes.featureGroupsEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.featureGroupsEditor.CLOSE: + return {}; + case actionTypes.featureGroupsEditor.SELECT_TAB: + return { + ...state, + selectedTab: action.tab + }; + + case actionTypes.featureGroupsEditor.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: + return { + ...state, + selectedEntitlementPoolsButtonTab: action.buttonTab + }; + case actionTypes.featureGroupsEditor.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: + return { + ...state, + selectedLicenseKeyGroupsButtonTab: action.buttonTab + }; + default: + return state; + } + +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js new file mode 100644 index 0000000000..5cf3248919 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './FeatureGroupsConstants.js'; +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FEATURE_GROUPS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.ADD_FEATURE_GROUPS: + return [...state, action.featureGroup]; + case actionTypes.EDIT_FEATURE_GROUPS: + const indexForEdit = state.findIndex(featureGroup => featureGroup.id === action.featureGroup.id); + return [...state.slice(0, indexForEdit), action.featureGroup, ...state.slice(indexForEdit + 1)]; + case actionTypes.DELETE_FEATURE_GROUPS: + return state.filter(featureGroup => featureGroup.id !== action.featureGroupId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js new file mode 100644 index 0000000000..9616b60b76 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants.js'; +import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; + +function baseUrl(licenseModelId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-agreements`; +} + +function fetchLicenseAgreementList(licenseModelId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +} + +function postLicenseAgreement(licenseModelId, licenseAgreement) { + return RestAPIUtil.create(baseUrl(licenseModelId), { + name: licenseAgreement.name, + description: licenseAgreement.description, + licenseTerm: licenseAgreement.licenseTerm, + requirementsAndConstrains: licenseAgreement.requirementsAndConstrains, + addedFeatureGroupsIds: licenseAgreement.featureGroupsIds + }); +} + +function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement) { + const {featureGroupsIds = []} = licenseAgreement; + const {featureGroupsIds: prevFeatureGroupsIds = []} = previousLicenseAgreement; + return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseAgreement.id}`, { + name: licenseAgreement.name, + description: licenseAgreement.description, + licenseTerm: licenseAgreement.licenseTerm, + requirementsAndConstrains: licenseAgreement.requirementsAndConstrains, + addedFeatureGroupsIds: featureGroupsIds.filter(featureGroupId => prevFeatureGroupsIds.indexOf(featureGroupId) === -1), + removedFeatureGroupsIds: prevFeatureGroupsIds.filter(prevFeatureGroupsId => featureGroupsIds.indexOf(prevFeatureGroupsId) === -1) + }); +} + +function deleteLicenseAgreement(licenseModelId, licenseAgreementId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseAgreementId}`); +} + +export default { + + fetchLicenseAgreementList(dispatch, {licenseModelId, version}) { + return fetchLicenseAgreementList(licenseModelId, version).then(response => dispatch({ + type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED, + response + })); + }, + + openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}) { + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId}); + dispatch({ + type: licenseAgreementActionTypes.licenseAgreementEditor.OPEN, + licenseAgreement + }); + }, + + licenseAgreementEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: licenseAgreementActionTypes.licenseAgreementEditor.DATA_CHANGED, + deltaData + }); + }, + + closeLicenseAgreementEditor(dispatch) { + dispatch({ + type: licenseAgreementActionTypes.licenseAgreementEditor.CLOSE + }); + }, + + + saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement}) { + if (previousLicenseAgreement) { + return putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement).then(() => { + dispatch({ + type: licenseAgreementActionTypes.EDIT_LICENSE_AGREEMENT, + licenseAgreement + }); + }); + } + else { + return postLicenseAgreement(licenseModelId, licenseAgreement).then(response => { + dispatch({ + type: licenseAgreementActionTypes.ADD_LICENSE_AGREEMENT, + licenseAgreement: { + ...licenseAgreement, + id: response.value + } + }); + }); + } + }, + + deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId}) { + return deleteLicenseAgreement(licenseModelId, licenseAgreementId).then(() => { + dispatch({ + type: licenseAgreementActionTypes.DELETE_LICENSE_AGREEMENT, + licenseAgreementId + }); + }); + }, + + selectLicenseAgreementEditorTab(dispatch, {tab}) { + dispatch({ + type: licenseAgreementActionTypes.licenseAgreementEditor.SELECT_TAB, + tab + }); + }, + + selectLicenseAgreementEditorFeatureGroupsButtonTab(dispatch, {buttonTab}) { + dispatch({ + type: licenseAgreementActionTypes.licenseAgreementEditor.SELECT_FEATURE_GROUPS_BUTTONTAB, + buttonTab + }); + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM, + licenseAgreementToDelete: false + }); + }, + + openDeleteLicenseAgreementConfirm(dispatch, {licenseAgreement} ) { + dispatch({ + type: licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM, + licenseAgreementToDelete: licenseAgreement + }); + }, + + switchVersion(dispatch, {licenseModelId, version}) { + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + this.fetchLicenseAgreementList(dispatch, {licenseModelId, version}); + }); + } +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx new file mode 100644 index 0000000000..42f2407696 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function renderMsg(licenseAgreementToDelete) { + let name = licenseAgreementToDelete ? licenseAgreementToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + return( +
      +

      {msg}

      +
      + ); +}; + +const mapStateToProps = ({licenseModel: {licenseAgreement}}, {licenseModelId}) => { + let {licenseAgreementToDelete} = licenseAgreement; + const show = licenseAgreementToDelete !== false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: renderMsg(licenseAgreementToDelete), + confirmationDetails: {licenseAgreementToDelete, licenseModelId} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({licenseAgreementToDelete, licenseModelId}) => { + + LicenseAgreementActionHelper.deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId: licenseAgreementToDelete.id}); + LicenseAgreementActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + LicenseAgreementActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js new file mode 100644 index 0000000000..af5c454e22 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + LICENSE_AGREEMENT_LIST_LOADED: null, + ADD_LICENSE_AGREEMENT: null, + EDIT_LICENSE_AGREEMENT: null, + DELETE_LICENSE_AGREEMENT: null, + LICENSE_AGREEMENT_DELETE_CONFIRM: null, + + licenseAgreementEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + SELECT_TAB: null, + SELECT_FEATURE_GROUPS_BUTTONTAB: null, + } + +}); + +export const enums = keyMirror({ + SELECTED_LICENSE_AGREEMENT_TAB: { + GENERAL: 1, + FEATURE_GROUPS: 2 + }, + + SELECTED_FEATURE_GROUPS_BUTTONTAB: { + ASSOCIATED_FEATURE_GROUPS: 1, + AVAILABLE_FEATURE_GROUPS: 2 + } +}); + +export const defaultState = { + LICENSE_AGREEMENT_EDITOR_DATA: { + licenseTerm: {choice: '', other: ''} + } +}; + +export const optionsInputValues = { + LICENSE_MODEL_TYPE: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Fixed_Term', title: 'Fixed Term'}, + {enum: 'Perpetual', title: 'Perpetual'}, + {enum: 'Unlimited', title: 'Unlimited'} + ] +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js new file mode 100644 index 0000000000..6a3e4dbc73 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js @@ -0,0 +1,60 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; +import LicenseAgreementEditorView from './LicenseAgreementEditorView.jsx'; + +export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}}) => { + + + let {data, selectedTab, selectedFeatureGroupsButtonTab} = licenseAgreement.licenseAgreementEditor; + + let previousData; + const licenseAgreementId = data ? data.id : null; + if(licenseAgreementId) { + previousData = licenseAgreement.licenseAgreementList.find(licenseAgreement => licenseAgreement.id === licenseAgreementId); + } + + const {featureGroupsList = []} = featureGroup; + + return { + data, + previousData, + selectedTab, + selectedFeatureGroupsButtonTab, + featureGroupsList + }; +}; + +export const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onDataChanged: deltaData => LicenseAgreementActionHelper.licenseAgreementEditorDataChanged(dispatch, {deltaData}), + onTabSelect: tab => LicenseAgreementActionHelper.selectLicenseAgreementEditorTab(dispatch, {tab}), + onFeatureGroupsButtonTabSelect: buttonTab => LicenseAgreementActionHelper.selectLicenseAgreementEditorFeatureGroupsButtonTab(dispatch, {buttonTab}), + onCancel: () => LicenseAgreementActionHelper.closeLicenseAgreementEditor(dispatch), + onSubmit: ({previousLicenseAgreement, licenseAgreement}) => { + LicenseAgreementActionHelper.closeLicenseAgreementEditor(dispatch); + LicenseAgreementActionHelper.saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseAgreementEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js new file mode 100644 index 0000000000..74e2f6e8c1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes, defaultState} from './LicenseAgreementConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.licenseAgreementEditor.OPEN: + return { + ...state, + data: action.licenseAgreement ? { ...action.licenseAgreement } : defaultState.LICENSE_AGREEMENT_EDITOR_DATA + }; + case actionTypes.licenseAgreementEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.licenseAgreementEditor.CLOSE: + return {}; + case actionTypes.licenseAgreementEditor.SELECT_TAB: + return { + ...state, + selectedTab: action.tab + }; + case actionTypes.licenseAgreementEditor.SELECT_FEATURE_GROUPS_BUTTONTAB: + return { + ...state, + selectedFeatureGroupsButtonTab: action.buttonTab + }; + default: + return state; + } + +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx new file mode 100644 index 0000000000..b21f943fed --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx @@ -0,0 +1,247 @@ +import React from 'react'; +import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js'; +import Button from 'react-bootstrap/lib/Button.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationTabs from 'nfvo-components/input/validation/ValidationTabs.jsx'; +import ValidationTab from 'nfvo-components/input/validation/ValidationTab.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorViewItem from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues} from './LicenseAgreementConstants.js'; + + +const LicenseAgreementPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + requirementsAndConstrains: React.PropTypes.string, + licenseTerm: React.PropTypes.object, + featureGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string) +}); + +class LicenseAgreementEditorView extends React.Component { + + static propTypes = { + data: LicenseAgreementPropType, + previousData: LicenseAgreementPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired, + + selectedTab: React.PropTypes.number, + onTabSelect: React.PropTypes.func, + + selectedFeatureGroupsButtonTab: React.PropTypes.number, + onFeatureGroupsButtonTabSelect: React.PropTypes.func, + featureGroupsList: DualListboxView.propTypes.availableList + }; + + static defaultProps = { + selectedTab: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL, + selectedFeatureGroupsButtonTab: LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS, + data: {} + }; + + state = { + localFeatureGroupsListFilter: '' + }; + + render() { + let {selectedTab, onTabSelect, isReadOnlyMode} = this.props; + return ( + this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + className='license-agreement-form'> + + {this.renderGeneralTab()} + {this.renderFeatureGroupsTab()} + + + ); + } + + submit() { + const {data: licenseAgreement, previousData: previousLicenseAgreement} = this.props; + this.props.onSubmit({licenseAgreement, previousLicenseAgreement}); + } + + renderGeneralTab() { + let {data = {}, onDataChanged} = this.props; + let {name, description, requirementsAndConstrains, licenseTerm} = data; + return ( + +
      +
      + onDataChanged({name})} + label={i18n('Name')} + value={name} + name='license-agreement-name' + validations={{maxLength: 25, required: true}} + type='text'/> + onDataChanged({requirementsAndConstrains})} + label={i18n('Requirements and Constraints')} + value={requirementsAndConstrains} + name='license-agreement-requirements-and-constraints' + validations={{maxLength: 1000}} + type='textarea'/> +
      + onDataChanged({description})} + label={i18n('Description')} + value={description} + name='license-agreement-description' + validations={{maxLength: 1000, required: true}} + type='textarea'/> +
      +
      + onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}})} + selectedEnum={licenseTerm && licenseTerm.choice} + validations={{required: true}} + type='select' + label={i18n('License Term')} + values={LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE}/> +
      +
      + ); + } + + renderFeatureGroupsTab() { + let {onFeatureGroupsButtonTabSelect, selectedFeatureGroupsButtonTab, featureGroupsList} = this.props; + if (featureGroupsList.length > 0) { + return ( + + + { + this.renderFeatureGroupsButtonTab( + LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.ASSOCIATED_FEATURE_GROUPS, + selectedFeatureGroupsButtonTab, + i18n('Associated Feature Groups'), + onFeatureGroupsButtonTabSelect + ) + } + { + this.renderFeatureGroupsButtonTab( + LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS, + selectedFeatureGroupsButtonTab, + i18n('Available Feature Groups'), + onFeatureGroupsButtonTabSelect + ) + } + + {this.renderFeatureGroupsButtonTabContent(selectedFeatureGroupsButtonTab)} + + ); + } else { + return ( + +

      {i18n('There is no available feature groups')}

      +
      + ); + } + } + + renderFeatureGroupsButtonTabContent(selectedFeatureGroupsButtonTab) { + const {featureGroupsList = [], data: {featureGroupsIds = []}} = this.props; + const {localFeatureGroupsListFilter} = this.state; + let selectedFeatureGroups = featureGroupsIds.map(featureGroupId => featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId)); + + const dualBoxFilterTitle = { + left: i18n('Available Feature Groups'), + right: i18n('Selected Feature Groups') + }; + + switch (selectedFeatureGroupsButtonTab) { + case LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.ASSOCIATED_FEATURE_GROUPS: + if (!selectedFeatureGroups.length) { + return ( +
      + {i18n('There are currently no feature groups associated with this license agreement. Click "Available Feature Groups" to associate.')} +
      + ); + } + if (featureGroupsList.length) { + return ( + this.setState({localFeatureGroupsListFilter})}> + {this.filterAssociatedFeatureGroupsList(selectedFeatureGroups).map(featureGroup => this.renderAssociatedFeatureGroupListItem(featureGroup))} + + ); + } + return; + case LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS: + return ( + this.props.onDataChanged( { featureGroupsIds: selectedValuesList } )}/> + ); + } + } + + renderFeatureGroupsButtonTab(buttonTab, selectedButtonTab, title, onClick) { + const isSelected = buttonTab === selectedButtonTab; + return ( + + ); + } + + renderAssociatedFeatureGroupListItem({id, name, entitlementPoolsIds = [], licenseKeyGroupsIds = []}) { + const {onDataChanged, data: {featureGroupsIds}, isReadOnlyMode} = this.props; + return ( + onDataChanged({featureGroupsIds: featureGroupsIds.filter(featureGroupId => featureGroupId !== id)})} + isReadOnlyMode={isReadOnlyMode}> +
      {name}
      +
      { + i18n( + 'Entitlement Pools({entitlementPoolsCounter}), License Key Groups({licenseKeyGroupsCount})', + { + entitlementPoolsCounter: entitlementPoolsIds.length, + licenseKeyGroupsCount: licenseKeyGroupsIds.length + } + ) + }
      +
      + ); + } + + filterAssociatedFeatureGroupsList(featureGroupsList) { + let {localFeatureGroupsListFilter} = this.state; + if (localFeatureGroupsListFilter) { + const filter = new RegExp(escape(localFeatureGroupsListFilter), 'i'); + return featureGroupsList.filter(({name}) => name.match(filter)); + } + else { + return featureGroupsList; + } + } +} + +export default LicenseAgreementEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js new file mode 100644 index 0000000000..ca18bfab79 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js'; +import LicenseAgreementListEditorView from './LicenseAgreementListEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; + +const mapStateToProps = ({licenseModel: {licenseAgreement, licenseModelEditor}}) => { + let {licenseAgreementList} = licenseAgreement; + let {data} = licenseAgreement.licenseAgreementEditor; + let {vendorName} = licenseModelEditor.data; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + return { + vendorName, + licenseAgreementList, + isReadOnlyMode, + isDisplayModal: Boolean(data), + isModalInEditMode: Boolean(data && data.id) + }; +}; + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onAddLicenseAgreementClick: () => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId}), + onEditLicenseAgreementClick: licenseAgreement => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}), + onDeleteLicenseAgreement: licenseAgreement => LicenseAgreementActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseAgreement}), + onCallVCAction: action => { + LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => { + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId}); + }); + }, + switchLicenseModelVersion: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}), + onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseAgreementListEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx new file mode 100644 index 0000000000..4d7e704ba3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx @@ -0,0 +1,126 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import LicenseAgreementEditor from './LicenseAgreementEditor.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import {optionsInputValues} from './LicenseAgreementConstants'; +import LicenseAgreementConfirmationModal from './LicenseAgreementConfirmationModal.jsx'; + + +class LicenseAgreementListEditorView extends React.Component { + static propTypes = { + vendorName: React.PropTypes.string, + licenseModelId: React.PropTypes.string.isRequired, + licenseAgreementList: React.PropTypes.array, + isReadOnlyMode: React.PropTypes.bool.isRequired, + isDisplayModal: React.PropTypes.bool, + isModalInEditMode: React.PropTypes.bool, + onAddLicenseAgreementClick: React.PropTypes.func, + onEditLicenseAgreementClick: React.PropTypes.func, + onDeleteLicenseAgreement: React.PropTypes.func, + onCallVCAction: React.PropTypes.func + }; + + static defaultProps = { + licenseAgreementList: [] + }; + + state = { + localFilter: '' + }; + + render() { + const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + const {onAddLicenseAgreementClick} = this.props; + const {localFilter} = this.state; + + return ( +
      + this.setState({localFilter: filter})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(licenseAgreement => this.renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode))} + + + + {`${isModalInEditMode ? i18n('Edit License Agreement') : i18n('Create New License Agreement')}`} + + + { + isDisplayModal && ( + + ) + } + + + + +
      + ); + } + + filterList() { + let {licenseAgreementList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return licenseAgreementList.filter(({name = '', description = '', licenseTerm = ''}) => { + return escape(name).match(filter) || escape(description).match(filter) || escape(this.extractValue(licenseTerm)).match(filter); + }); + } + else { + return licenseAgreementList; + } + } + + renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode) { + let {id, name, description, licenseTerm, featureGroupsIds = []} = licenseAgreement; + let {onEditLicenseAgreementClick, onDeleteLicenseAgreement} = this.props; + return ( + onEditLicenseAgreementClick(licenseAgreement)} + onDelete={() => onDeleteLicenseAgreement(licenseAgreement)} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode}> +
      +
      {i18n('Name')}
      +
      {name}
      +
      +
      +
      +
      {i18n('Type')}
      +
      {this.extractValue(licenseTerm)}
      +
      +
      +
      {i18n('Feature')}
      +
      {i18n('Groups')}
      +
      {featureGroupsIds.length}
      +
      +
      +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      + ); + } + + extractValue(item) { + if (item === undefined) { + return ''; + } //TODO fix it later + + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; + } +} + +export default LicenseAgreementListEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js new file mode 100644 index 0000000000..5b5fa00df1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants'; + +export default (state = [], action) => { + switch (action.type) { + case licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED: + return [...action.response.results]; + case licenseAgreementActionTypes.ADD_LICENSE_AGREEMENT: + return [...state, action.licenseAgreement]; + case licenseAgreementActionTypes.EDIT_LICENSE_AGREEMENT: + const indexForEdit = state.findIndex(licenseAgreement => licenseAgreement.id === action.licenseAgreement.id); + return [...state.slice(0, indexForEdit), action.licenseAgreement, ...state.slice(indexForEdit + 1)]; + case licenseAgreementActionTypes.DELETE_LICENSE_AGREEMENT: + return state.filter(licenseAgreement => licenseAgreement.id !== action.licenseAgreementId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js new file mode 100644 index 0000000000..50ac2c85a3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js @@ -0,0 +1,139 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes as licenseKeyGroupsConstants} from './LicenseKeyGroupsConstants.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; + +function baseUrl(licenseModelId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-key-groups`; +} + +function fetchLicenseKeyGroupsList(licenseModelId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`); +} + +function deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId) { + return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseKeyGroupId}`); +} + +function postLicenseKeyGroup(licenseModelId, licenseKeyGroup) { + return RestAPIUtil.create(baseUrl(licenseModelId), { + name: licenseKeyGroup.name, + description: licenseKeyGroup.description, + operationalScope: licenseKeyGroup.operationalScope, + type: licenseKeyGroup.type + }); +} + +function putLicenseKeyGroup(licenseModelId, licenseKeyGroup) { + return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseKeyGroup.id}`, { + name: licenseKeyGroup.name, + description: licenseKeyGroup.description, + operationalScope: licenseKeyGroup.operationalScope, + type: licenseKeyGroup.type + }); +} + + +export default { + fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}) { + return fetchLicenseKeyGroupsList(licenseModelId, version).then(response => dispatch({ + type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_LIST_LOADED, + response + })); + }, + + openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup} = {}) { + dispatch({ + type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.OPEN, + licenseKeyGroup + }); + }, + + closeLicenseKeyGroupEditor(dispatch){ + dispatch({ + type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.CLOSE + }); + }, + + saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}) { + if (previousLicenseKeyGroup) { + return putLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(() => { + dispatch({ + type: licenseKeyGroupsConstants.EDIT_LICENSE_KEY_GROUP, + licenseKeyGroup + }); + }); + } + else { + return postLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(response => { + dispatch({ + type: licenseKeyGroupsConstants.ADD_LICENSE_KEY_GROUP, + licenseKeyGroup: { + ...licenseKeyGroup, + id: response.value + } + }); + }); + } + + + }, + + deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId}){ + return deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId).then(()=> { + dispatch({ + type: licenseKeyGroupsConstants.DELETE_LICENSE_KEY_GROUP, + licenseKeyGroupId + }); + }); + }, + + licenseKeyGroupEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.DATA_CHANGED, + deltaData + }); + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM, + licenseKeyGroupToDelete: false + }); + }, + + openDeleteLicenseAgreementConfirm(dispatch, {licenseKeyGroup}) { + dispatch({ + type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM, + licenseKeyGroupToDelete: licenseKeyGroup + }); + }, + + switchVersion(dispatch, {licenseModelId, version}) { + LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => { + this.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}); + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx new file mode 100644 index 0000000000..2413db51d0 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function renderMsg(licenseKeyGroupToDelete) { + let name = licenseKeyGroupToDelete ? licenseKeyGroupToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + let subMsg = licenseKeyGroupToDelete + && licenseKeyGroupToDelete.referencingFeatureGroups + && licenseKeyGroupToDelete.referencingFeatureGroups.length > 0 ? + i18n('This license key group is associated with one or more feature groups') : + ''; + return( +
      +

      {msg}

      +

      {subMsg}

      +
      + ); +}; + +const mapStateToProps = ({licenseModel: {licenseKeyGroup}}, {licenseModelId}) => { + let {licenseKeyGroupToDelete} = licenseKeyGroup; + const show = licenseKeyGroupToDelete !== false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: renderMsg(licenseKeyGroupToDelete), + confirmationDetails: {licenseKeyGroupToDelete, licenseModelId} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({licenseKeyGroupToDelete, licenseModelId}) => { + + LicenseKeyGroupsActionHelper.deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId:licenseKeyGroupToDelete.id}); + LicenseKeyGroupsActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + LicenseKeyGroupsActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js new file mode 100644 index 0000000000..d32bc52744 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + + LICENSE_KEY_GROUPS_LIST_LOADED: null, + DELETE_LICENSE_KEY_GROUP: null, + EDIT_LICENSE_KEY_GROUP: null, + ADD_LICENSE_KEY_GROUP: null, + LICENSE_KEY_GROUPS_DELETE_CONFIRM: null, + licenseKeyGroupsEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + } +}); + +export const defaultState = { + licenseKeyGroupsEditor: { + type: '', + operationalScope: {choices: [], other: ''} + } +}; + +export const optionsInputValues = { + OPERATIONAL_SCOPE: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Network_Wide', title: 'Network Wide'}, + {enum: 'Availability_Zone', title: 'Availability Zone'}, + {enum: 'Data_Center', title: 'Data Center'}, + {enum: 'Tenant', title: 'Tenant'}, + {enum: 'VM', title: 'VM'}, + {enum: 'CPU', title: 'CPU'}, + {enum: 'Core', title: 'Core'} + ], + TYPE: [ + {enum: '', title: i18n('please select…')}, + {enum: 'Universal', title: 'Universal'}, + {enum: 'Unique', title: 'Unique'}, + {enum: 'One_Time', title: 'One Time'} + ] +}; + + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js new file mode 100644 index 0000000000..3940ec594a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; +import LicenseKeyGroupsEditorView from './LicenseKeyGroupsEditorView.jsx'; + +const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => { + + + let {data} = licenseKeyGroup.licenseKeyGroupsEditor; + + let previousData; + const licenseKeyGroupId = data ? data.id : null; + if(licenseKeyGroupId) { + previousData = licenseKeyGroup.licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId); + } + + return { + data, + previousData + }; +}; + +const mapActionsToProps = (dispatch, {licenseModelId}) => { + return { + onDataChanged: deltaData => LicenseKeyGroupsActionHelper.licenseKeyGroupEditorDataChanged(dispatch, {deltaData}), + onCancel: () => LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch), + onSubmit: ({previousLicenseKeyGroup, licenseKeyGroup}) => { + LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch); + LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseKeyGroupsEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js new file mode 100644 index 0000000000..a74498269a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes, defaultState} from './LicenseKeyGroupsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.licenseKeyGroupsEditor.OPEN: + return { + ...state, + data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor + }; + case actionTypes.licenseKeyGroupsEditor.CLOSE: + return {}; + case actionTypes.licenseKeyGroupsEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx new file mode 100644 index 0000000000..102e713060 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx @@ -0,0 +1,92 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import {optionsInputValues as licenseKeyGroupOptionsInputValues} from './LicenseKeyGroupsConstants.js'; +import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; + +const LicenseKeyGroupPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + operationalScope: React.PropTypes.shape({ + choices: React.PropTypes.array, + other: React.PropTypes.string + }), + type: React.PropTypes.string +}); + +class LicenseKeyGroupsEditorView extends React.Component { + static propTypes = { + data: LicenseKeyGroupPropType, + previousData: LicenseKeyGroupPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + static defaultProps = { + data: {} + }; + + render() { + let {data = {}, onDataChanged, isReadOnlyMode} = this.props; + let {name, description, operationalScope, type} = data; + return ( + this.submit() } + onReset={ () => this.props.onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + className='license-key-groups-form'> +
      + onDataChanged({name})} + ref='name' + label={i18n('Name')} + value={name} + validations={{maxLength: 120, required: true}} + type='text'/> + onDataChanged({operationalScope:{choices: operationalScope, other: ''}})} + onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], other: operationalScope}})} + label={i18n('Operational Scope')} + validations={{required: true}} + multiSelectedEnum={operationalScope && operationalScope.choices} + otherValue={operationalScope && operationalScope.other} + values={licenseKeyGroupOptionsInputValues.OPERATIONAL_SCOPE}/> +
      +
      + onDataChanged({description})} + ref='description' + label={i18n('Description')} + value={description} + validations={{maxLength: 1000, required: true}} + type='textarea'/> + onDataChanged({type})} + selectedEnum={type} + label={i18n('Type')} + type='select' + validations={{required: true}} + values={licenseKeyGroupOptionsInputValues.TYPE}/> +
      +
      + ); + } + + submit() { + const {data: licenseKeyGroup, previousData: previousLicenseKeyGroup} = this.props; + this.props.onSubmit({licenseKeyGroup, previousLicenseKeyGroup}); + } +} + +export default LicenseKeyGroupsEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js new file mode 100644 index 0000000000..e1b610f973 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js @@ -0,0 +1,50 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js'; +import LicenseKeyGroupsListEditorView from './LicenseKeyGroupsListEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) => { + let {licenseKeyGroupsList} = licenseKeyGroup; + let {data} = licenseKeyGroup.licenseKeyGroupsEditor; + let {vendorName} = licenseModelEditor.data; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data); + + return { + vendorName, + licenseKeyGroupsList, + isReadOnlyMode, + isDisplayModal: Boolean(data), + isModalInEditMode: Boolean(data && data.id) + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onAddLicenseKeyGroupClick: () => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch), + onEditLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup}), + onDeleteLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseKeyGroup}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(LicenseKeyGroupsListEditorView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx new file mode 100644 index 0000000000..1ed1d2093a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx @@ -0,0 +1,138 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import LicenseKeyGroupsEditor from './LicenseKeyGroupsEditor.js'; +import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx'; +import {optionsInputValues} from './LicenseKeyGroupsConstants'; +import LicenseKeyGroupsConfirmationModal from './LicenseKeyGroupsConfirmationModal.jsx'; + + +class LicenseKeyGroupsListEditorView extends React.Component { + static propTypes = { + vendorName: React.PropTypes.string, + licenseModelId: React.PropTypes.string.isRequired, + licenseKeyGroupsList: React.PropTypes.array, + isReadOnlyMode: React.PropTypes.bool.isRequired, + isDisplayModal: React.PropTypes.bool, + isModalInEditMode: React.PropTypes.bool, + onAddLicenseKeyGroupClick: React.PropTypes.func, + onEditLicenseKeyGroupClick: React.PropTypes.func, + onDeleteLicenseKeyGroupClick: React.PropTypes.func + }; + + static defaultProps = { + licenseKeyGroupsList: [] + }; + + state = { + localFilter: '' + }; + + render() { + let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + let {onAddLicenseKeyGroupClick} = this.props; + const {localFilter} = this.state; + + return ( +
      + this.setState({localFilter: filter})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(licenseKeyGroup => (this.renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode)))} + + + + {`${isModalInEditMode ? i18n('Edit License Key Group') : i18n('Create New License Key Group')}`} + + + { + isDisplayModal && ( + + ) + } + + + + +
      + ); + } + + filterList() { + let {licenseKeyGroupsList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return licenseKeyGroupsList.filter(({name = '', description = '', operationalScope = '', type = ''}) => { + return escape(name).match(filter) || escape(description).match(filter) || escape(this.extractValue(operationalScope)).match(filter) || escape(type).match(filter); + }); + } + else { + return licenseKeyGroupsList; + } + } + + renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode) { + let {id, name, description, operationalScope, type} = licenseKeyGroup; + let {onEditLicenseKeyGroupClick, onDeleteLicenseKeyGroupClick} = this.props; + return ( + onEditLicenseKeyGroupClick(licenseKeyGroup)} + onDelete={() => onDeleteLicenseKeyGroupClick(licenseKeyGroup)} + className='list-editor-item-view' + isReadOnlyMode={isReadOnlyMode}> +
      +
      {i18n('Name')}
      +
      {name}
      +
      + +
      +
      {i18n('Operational Scope')}
      +
      {operationalScope && this.getOperationalScopes(operationalScope)}
      + +
      {i18n('Type')}
      +
      {InputOptions.getTitleByName(optionsInputValues, type)}
      +
      +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      + ); + } + + getOperationalScopes(operationalScope) { + if(operationalScope.choices.toString() === i18n(optionInputOther.OTHER) && operationalScope.other !== '') { + return operationalScope.other; + } + else { + let allOpScopes = ''; + for (let opScope of operationalScope.choices) { + allOpScopes += allOpScopes === '' ? InputOptions.getTitleByName(optionsInputValues, opScope) : `, ${InputOptions.getTitleByName(optionsInputValues, opScope)}`; + } + return allOpScopes; + } + } + + extractValue(item) { + if (item === undefined) { + return ''; + } //TODO fix it later + + return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : ''; + } +} + +export default LicenseKeyGroupsListEditorView; + + + diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js new file mode 100644 index 0000000000..54ce4e3955 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './LicenseKeyGroupsConstants.js'; +export default (state = [], action) => { + switch (action.type) { + case actionTypes.LICENSE_KEY_GROUPS_LIST_LOADED: + return [...action.response.results]; + case actionTypes.DELETE_LICENSE_KEY_GROUP: + return state.filter(licenseKeyGroup => licenseKeyGroup.id !== action.licenseKeyGroupId); + case actionTypes.ADD_LICENSE_KEY_GROUP: + return [...state, action.licenseKeyGroup]; + case actionTypes.EDIT_LICENSE_KEY_GROUP: + const indexForEdit = state.findIndex(licenseKeyGroup => licenseKeyGroup.id === action.licenseKeyGroup.id); + return [...state.slice(0, indexForEdit), action.licenseKeyGroup, ...state.slice(indexForEdit + 1)]; + default: + return state; + } +}; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js new file mode 100644 index 0000000000..2dbef6baf2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js @@ -0,0 +1,321 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx'; + +import {enums} from 'sdc-app/onboarding/OnboardingConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; + +import {navigationItems} from './SoftwareProductConstants.js'; +import SoftwareProductActionHelper from './SoftwareProductActionHelper.js'; +import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; + +const buildComponentNavigationBarGroups = ({componentId, meta}) => { + const groups = ([ + { + id: navigationItems.GENERAL + '|' + componentId, + name: i18n('General'), + disabled: false, + meta + }, { + id: navigationItems.COMPUTE + '|' + componentId, + name: i18n('Compute'), + disabled: false, + meta + }, { + id: navigationItems.LOAD_BALANCING + '|' + componentId, + name: i18n('High Availability & Load Balancing'), + disabled: false, + meta + }, { + id: navigationItems.NETWORKS + '|' + componentId, + name: i18n('Networks'), + disabled: false, + meta + }, { + id: navigationItems.STORAGE + '|' + componentId, + name: i18n('Storage'), + disabled: false, + meta + }, { + id: navigationItems.PROCESS_DETAILS + '|' + componentId, + name: i18n('Process Details'), + disabled: false, + meta + }, { + id: navigationItems.MONITORING + '|' + componentId, + name: i18n('Monitoring'), + disabled: false, + meta + } + ]); + + return groups; +}; + +const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) => { + const {softwareProductEditor: {data: currentSoftwareProduct = {}}} = softwareProduct; + const {id, name} = currentSoftwareProduct; + const groups = [{ + id: id, + name: name, + items: [ + { + id: navigationItems.VENDOR_SOFTWARE_PRODUCT, + name: i18n('Overview'), + disabled: false, + meta + }, { + id: navigationItems.GENERAL, + name: i18n('General'), + disabled: false, + meta + }, { + id: navigationItems.PROCESS_DETAILS, + name: i18n('Process Details'), + disabled: false, + meta + }, { + id: navigationItems.NETWORKS, + name: i18n('Networks'), + disabled: false, + meta + }, { + id: navigationItems.ATTACHMENTS, + name: i18n('Attachments'), + disabled: false, + meta + }, { + id: navigationItems.COMPONENTS, + name: i18n('Components'), + hidden: componentsList.length <= 0, + meta, + expanded: mapOfExpandedIds[navigationItems.COMPONENTS] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, + items: [ + ...componentsList.map(({id, displayName}) => ({ + id: navigationItems.COMPONENTS + '|' + id, + name: displayName, + meta, + expanded: mapOfExpandedIds[navigationItems.COMPONENTS + '|' + id] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, + items: buildComponentNavigationBarGroups({componentId: id, meta}) + })) + ] + } + ] + }]; + let activeItemId = ({ + [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: navigationItems.VENDOR_SOFTWARE_PRODUCT, + [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: navigationItems.GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: navigationItems.ATTACHMENTS, + [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: navigationItems.COMPONENTS + })[screen]; + + if(componentId) { + activeItemId = + Object.keys(mapOfExpandedIds).length === 1 && mapOfExpandedIds[navigationItems.COMPONENTS] === true ? + navigationItems.COMPONENTS : ({ + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: navigationItems.GENERAL, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: navigationItems.COMPUTE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: navigationItems.LOAD_BALANCING, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: navigationItems.NETWORKS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS, + [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING + })[screen] + '|' + componentId; + } + + return { + activeItemId, groups + }; +}; + +const buildVersionControllerProps = (softwareProduct) => { + const {softwareProductEditor} = softwareProduct; + const {data: currentSoftwareProduct = {}, isValidityData = true} = softwareProductEditor; + + const {version, viewableVersions, status: currentStatus, lockingUser} = currentSoftwareProduct; + const {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ? + VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) : + {status: currentStatus, isCheckedOut: false}; + + return { + status, isCheckedOut, version, viewableVersions, + isFormDataValid: isValidityData + }; +}; + +const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => { + const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire} = softwareProduct; + const {data: currentSoftwareProduct = {}, mapOfExpandedIds = []} = softwareProductEditor; + const {version} = currentSoftwareProduct; + const {componentsList = []} = softwareProductComponents; + const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + const {qdata} = softwareProductQuestionnaire; + let currentComponentMeta = {}; + if(componentId) { + const {componentEditor: {data: componentData = {} , qdata: componentQdata}} = softwareProductComponents; + currentComponentMeta = {componentData, componentQdata}; + } + const meta = {softwareProduct: currentSoftwareProduct, qdata, version, isReadOnlyMode, currentComponentMeta}; + return { + versionControllerProps: buildVersionControllerProps(softwareProduct), + navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) + }; +}; + +const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, meta: {isReadOnlyMode, softwareProduct, qdata, currentComponentMeta: {componentData, componentQdata}}}) => { + let promise; + if (isReadOnlyMode) { + promise = Promise.resolve(); + } else { + switch(screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS: + promise = SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata}); + break; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL: + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, componentData, qdata: componentQdata}); + break; + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: + promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata: componentQdata}); + break; + default: + promise = Promise.resolve(); + break; + } + } + return promise; +}; + + +const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentComponentId}) => { + const [nextScreen, nextComponentId] = id.split('|'); + switch(nextScreen) { + case navigationItems.COMPONENTS: + if(nextComponentId === currentComponentId) { + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId}); + } else { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version}); + } + break; + case navigationItems.GENERAL: + OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version}); + break; + case navigationItems.COMPUTE: + OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.LOAD_BALANCING: + OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.NETWORKS: + OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version}); + break; + case navigationItems.STORAGE: + OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.PROCESS_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + case navigationItems.MONITORING: + OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId}); + break; + } +}; + +const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwareProductId, componentId: currentComponentId}}}) => { + + const props = { + onClose: ({version}) => { + if (screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE) { + OnboardingActionHelper.navigateToOnboardingCatalog(dispatch); + } else { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); + } + }, + onVersionSwitching: (version) => { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version}); + }, + onToggle: (groups, itemIdToExpand) => groups.map(({items}) => SoftwareProductActionHelper.toggleNavigationItems(dispatch, {items, itemIdToExpand})), + onNavigate: ({id, meta}) => { + let preNavigate = autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId}); + preNavigate.then(() => { + switch(id) { + case navigationItems.VENDOR_SOFTWARE_PRODUCT: + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: meta.version}); + break; + case navigationItems.GENERAL: + OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId}); + break; + case navigationItems.PROCESS_DETAILS: + OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: meta.version}); + break; + case navigationItems.NETWORKS: + OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: meta.version}); + break; + case navigationItems.ATTACHMENTS: + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + break; + case navigationItems.COMPONENTS: + OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId}); + break; + default: + onComponentNavigate(dispatch, {id, softwareProductId, version: meta.version, screen, currentComponentId}); + break; + } + }); + } + }; + + switch (screen) { + case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE: + case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES: + case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING: + props.onSave = () => { + return Promise.resolve(); + }; + break; + default: + props.onSave = ({softwareProduct, qdata}) => SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata}); + break; + } + + + props.onVersionControllerAction = (action) => + SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action}).then(() => { + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); + }); + + return props; +}; + +export default connect(mapStateToProps, mapActionsToProps)(TabulatedEditor); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js new file mode 100644 index 0000000000..d9ed8af679 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js @@ -0,0 +1,333 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js'; +import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; +import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; + +import {actionTypes} from './SoftwareProductConstants.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js'; +import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/`; +} +function softwareProductCategoriesUrl() { + const restATTPrefix = Configuration.get('restATTPrefix'); + return `${restATTPrefix}/v1/categories/resources/`; +} + +function uploadFile(vspId, formData) { + + return RestAPIUtil.create(`${baseUrl()}${vspId}/upload`, formData); + +} + +function putSoftwareProduct(softwareData) { + return RestAPIUtil.save(`${baseUrl()}${softwareData.id}`, { + name: softwareData.name, + description: softwareData.description, + category: softwareData.category, + subCategory: softwareData.subCategory, + vendorId: softwareData.vendorId, + vendorName: softwareData.vendorName, + licensingVersion: softwareData.licensingVersion, + icon: softwareData.icon, + licensingData: softwareData.licensingData + }); +} + +function putSoftwareProductQuestionnaire(vspId, qdata) { + return RestAPIUtil.save(`${baseUrl()}${vspId}/questionnaire`, qdata); +} + +function putSoftwareProductAction(id, action) { + return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action}); +} + +function fetchSoftwareProductList() { + return RestAPIUtil.fetch(baseUrl()); +} + +function fetchSoftwareProduct(vspId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl()}${vspId}${versionQuery}`); +} + +function fetchSoftwareProductQuestionnaire(vspId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl()}${vspId}/questionnaire${versionQuery}`); +} + +function objToString(obj) { + let str = ''; + if (obj instanceof Array) { + obj.forEach((item) => { + str += objToString(item) + '\n'; + }); + } else { + for (let p in obj) { + if (obj.hasOwnProperty(p)) { + str += obj[p] + '\n'; + } + } + } + return str; +} + +function parseUploadErrorMsg(error) { + let message = ''; + for (let key in error) { + if (error.hasOwnProperty(key)) { + message += objToString(error[key]) + '\n'; + } + } + return message; +} + +function fetchSoftwareProductCategories(dispatch) { + let handleResponse = response => dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED, + softwareProductCategories: response + }); + return RestAPIUtil.fetch(softwareProductCategoriesUrl()) + .then(handleResponse) + .fail(() => handleResponse(null)); +} + +function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) { + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}); + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion}); +} + +function getExpandedItemsId(items, itemIdToToggle) { + for(let i = 0; i < items.length; i++) { + if(items[i].id === itemIdToToggle) { + if (items[i].expanded) { + return {}; + } else { + return {[itemIdToToggle]: true}; + } + } + else if(items[i].items && items[i].items.length > 0) { + let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle); + if (mapOfExpandedIds !== false) { + mapOfExpandedIds[items[i].id] = true; + return mapOfExpandedIds; + } + } + } + return false; +} + +const SoftwareProductActionHelper = { + + loadSoftwareProductAssociatedData(dispatch) { + fetchSoftwareProductCategories(dispatch); + LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch); + }, + + loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) { + SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); + loadLicensingData(dispatch, {licenseModelId, licensingVersion}); + }, + + fetchSoftwareProductList(dispatch) { + return fetchSoftwareProductList().then(response => dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED, + response + })); + }, + + uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle}) { + Promise.resolve() + .then(() => uploadFile(softwareProductId, formData)) + .then(response => { + if (response.status !== 'Success') { + throw new Error(parseUploadErrorMsg(response.errors)); + } + }) + .then(() => { + SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId}); + OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}); + SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId}); + }) + .catch(error => { + dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {title: failedNotificationTitle, msg: error.message} + }); + }); + }, + + uploadConfirmation(dispatch, {softwareProductId, formData, failedNotificationTitle}) { + dispatch({ + type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION, + uploadData: { + softwareProductId, + formData, + failedNotificationTitle + } + }); + }, + hideUploadConfirm (dispatch) { + dispatch({ + type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION + }); + }, + updateSoftwareProduct(dispatch, {softwareProduct, qdata}) { + return Promise.all([ + SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then( + () => dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT, + payload: {softwareProduct} + }) + ), + SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, { + softwareProductId: softwareProduct.id, + qdata + }) + ]); + }, + + updateSoftwareProductData(dispatch, {softwareProduct}) { + return putSoftwareProduct(softwareProduct); + }, + + updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata}) { + return putSoftwareProductQuestionnaire(softwareProductId, qdata); + }, + + softwareProductEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.softwareProductEditor.DATA_CHANGED, + deltaData + }); + }, + + softwareProductQuestionnaireUpdate(dispatch, {data}) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE, + payload: {qdata: data} + }); + }, + + softwareProductEditorVendorChanged(dispatch, {deltaData}) { + LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion}); + FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion}); + SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}); + }, + + setIsValidityData(dispatch, {isValidityData}) { + dispatch({ + type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED, + isValidityData + }); + }, + + addSoftwareProduct(dispatch, {softwareProduct}) { + dispatch({ + type: actionTypes.ADD_SOFTWARE_PRODUCT, + softwareProduct + }); + }, + + fetchSoftwareProduct(dispatch, {softwareProductId, version}) { + return Promise.all([ + fetchSoftwareProduct(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + return response; + }), + fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE, + payload: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + } + }); + }) + ]); + }, + + performVCAction(dispatch, {softwareProductId, action}) { + if (action === VersionControllerActionsEnum.SUBMIT) { + return putSoftwareProductAction(softwareProductId, action).then(() => { + return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE).then(() => { + dispatch({ + type: NotificationConstants.NOTIFY_SUCCESS, + data: { + title: i18n('Submit Succeeded'), + msg: i18n('This software product successfully submitted'), + timeout: 2000 + } + }); + fetchSoftwareProduct(softwareProductId).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + }); + }); + }, error => dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: {title: i18n('Submit Failed'), validationResponse: error.responseJSON} + })); + } + else { + return putSoftwareProductAction(softwareProductId, action).then(() => { + fetchSoftwareProduct(softwareProductId).then(response => { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_LOADED, + response + }); + }); + }); + } + }, + + switchVersion(dispatch, {softwareProductId, licenseModelId, version}) { + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version}); + }, + + toggleNavigationItems(dispatch, {items, itemIdToExpand}) { + let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand); + dispatch({ + type: actionTypes.TOGGLE_NAVIGATION_ITEM, + mapOfExpandedIds + }); + }, + + /** for the next verision */ + addComponent(dispatch) { + return dispatch; + } +}; + +export default SoftwareProductActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js new file mode 100644 index 0000000000..812afe5409 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +export default { + + getCurrentCategoryOfSubCategory(selectedSubCategory, softwareProductCategories) { + let category, subCategory; + for (var i = 0; i < softwareProductCategories.length; i++) { + let {subcategories = []} = softwareProductCategories[i]; + subCategory = subcategories.find(sub => sub.uniqueId === selectedSubCategory); + if (subCategory) { + category = softwareProductCategories[i].uniqueId; + break; + } + } + return category; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js new file mode 100644 index 0000000000..5f10c27084 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + SOFTWARE_PRODUCT_LOADED: null, + SOFTWARE_PRODUCT_LIST_LOADED: null, + SOFTWARE_PRODUCT_LIST_EDIT: null, + SOFTWARE_PRODUCT_CATEGORIES_LOADED: null, + SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE: null, + ADD_SOFTWARE_PRODUCT: null, + TOGGLE_NAVIGATION_ITEM: null, + + softwareProductEditor: { + OPEN: null, + CLOSE: null, + DATA_CHANGED: null, + IS_VALIDITY_DATA_CHANGED: null, + UPLOAD_CONFIRMATION: null + } +}); + +export const navigationItems = keyMirror({ + VENDOR_SOFTWARE_PRODUCT: 'Vendor Software Product', + GENERAL: 'General', + PROCESS_DETAILS: 'Process Details', + NETWORKS: 'Networks', + ATTACHMENTS: 'Attachments', + COMPONENTS: 'Components', + + COMPUTE: 'Compute', + LOAD_BALANCING: 'Load Balancing', + STORAGE: 'Storage', + MONITORING: 'Monitoring' +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js new file mode 100644 index 0000000000..6d1db1626f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_LIST_LOADED: + return [...action.response.results]; + case actionTypes.SOFTWARE_PRODUCT_LIST_EDIT: + const indexForEdit = state.findIndex(vsp => vsp.id === action.payload.softwareProduct.id); + return [...state.slice(0, indexForEdit), action.payload.softwareProduct, ...state.slice(indexForEdit + 1)]; + case actionTypes.ADD_SOFTWARE_PRODUCT: + return [...state, action.softwareProduct]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js new file mode 100644 index 0000000000..784ac9db84 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {combineReducers} from 'redux'; +import {actionTypes} from './SoftwareProductConstants.js'; +import SoftwareProductAttachmentsReducer from './attachments/SoftwareProductAttachmentsReducer.js'; +import SoftwareProductCreationReducer from './creation/SoftwareProductCreationReducer.js'; +import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js'; +import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js'; +import SoftwareProductProcessesEditorReducer from './processes/SoftwareProductProcessesEditorReducer.js'; +import SoftwareProductNetworksListReducer from './networks/SoftwareProductNetworksListReducer.js'; +import SoftwareProductComponentsListReducer from './components/SoftwareProductComponentsListReducer.js'; +import SoftwareProductComponentEditorReducer from './components/SoftwareProductComponentEditorReducer.js'; +import {actionTypes as processesActionTypes} from './processes/SoftwareProductProcessesConstants.js'; +import SoftwareProductComponentProcessesListReducer from './components/processes/SoftwareProductComponentProcessesListReducer.js'; +import SoftwareProductComponentProcessesEditorReducer from './components/processes/SoftwareProductComponentProcessesEditorReducer.js'; +import {actionTypes as componentProcessesActionTypes} from './components/processes/SoftwareProductComponentProcessesConstants.js'; +import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js'; +import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js'; +import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js'; + +export default combineReducers({ + softwareProductAttachments: SoftwareProductAttachmentsReducer, + softwareProductCreation: SoftwareProductCreationReducer, + softwareProductEditor: SoftwareProductDetailsReducer, + softwareProductProcesses: combineReducers({ + processesList: SoftwareProductProcessesListReducer, + processesEditor: SoftwareProductProcessesEditorReducer, + processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state + }), + softwareProductNetworks: combineReducers({ + networksList: SoftwareProductNetworksListReducer + }), + softwareProductComponents: combineReducers({ + componentsList: SoftwareProductComponentsListReducer, + componentEditor: SoftwareProductComponentEditorReducer, + componentProcesses: combineReducers({ + processesList: SoftwareProductComponentProcessesListReducer, + processesEditor: SoftwareProductComponentProcessesEditorReducer, + processToDelete: (state = false, action) => action.type === componentProcessesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM ? action.processToDelete : state, + }), + network: combineReducers({ + nicList: SoftwareProductComponentsNICListReducer, + nicEditor: SoftwareProductComponentsNICEditorReducer + }), + monitoring: SoftwareProductComponentsMonitoringReducer + }), + softwareProductCategories: (state = [], action) => { + if (action.type === actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED) { + return action.softwareProductCategories; + } + return state; + }, + softwareProductQuestionnaire: (state = {}, action) => { + if (action.type === actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE) { + return { + ...state, + ...action.payload + }; + } + return state; + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js new file mode 100644 index 0000000000..a4b95a4b7e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductAttachmentsView from './SoftwareProductAttachmentsView.jsx'; +import SoftwareProductAttachmentsActionHelper from './SoftwareProductAttachmentsActionHelper.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments}}) => { + let {attachmentsTree, hoveredNode, selectedNode, errorList} = softwareProductAttachments; + return { + attachmentsTree, + hoveredNode, + selectedNode, + errorList + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + toggleExpanded: (path) => SoftwareProductAttachmentsActionHelper.toggleExpanded(dispatch, {path}), + onSelectNode: (nodeName) => SoftwareProductAttachmentsActionHelper.onSelectNode(dispatch, {nodeName}), + onUnselectNode: () => SoftwareProductAttachmentsActionHelper.onUnselectNode(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductAttachmentsView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js new file mode 100644 index 0000000000..a7f7a5173b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; + +export default { + + toggleExpanded(dispatch, {path}) { + dispatch({ + type: actionTypes.TOGGLE_EXPANDED, + path + }); + }, + + onSelectNode(dispatch, {nodeName}) { + dispatch({ + type: actionTypes.SELECTED_NODE, + nodeName + }); + }, + + onUnselectNode(dispatch) { + dispatch({ + type: actionTypes.UNSELECTED_NODE + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js new file mode 100644 index 0000000000..33af476d9c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + TOGGLE_EXPANDED: null, + SELECTED_NODE: null, + UNSELECTED_NODE: null +}); + +export const errorTypes = keyMirror({ + MISSING_FILE_IN_ZIP: i18n('missing file in zip'), + MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'), + MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'), + FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'), + FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'), + ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'), + ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'), + MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'), + MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'), + ARTIFACT_NOT_IN_USE: i18n('artifact not in use') +}); + +export const nodeTypes = keyMirror({ + heat: i18n('Heat'), + volume: i18n('Volume'), + network: i18n('Network'), + artifact: i18n('Artifact'), + env: i18n('Environment'), + other: i18n('') +}); + +export const mouseActions = keyMirror({ + MOUSE_BUTTON_CLICK: 0 +}); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js new file mode 100644 index 0000000000..5c5567b032 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js @@ -0,0 +1,199 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes} from './SoftwareProductAttachmentsConstants.js'; + +const mapVolumeData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'volume', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapNetworkData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'network', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapArtifactsData = ({fileName, errors}) => ({ + name: fileName, + type: 'artifact', + errors +}); + +const mapOtherData = ({fileName, errors}) => ({ + name: fileName, + type: 'other', + errors +}); + + +const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({ + name: fileName, + expanded: true, + type: 'heat', + errors, + children: [ + ...(volume ? volume.map(mapVolumeData) : []), + ...(network ? network.map(mapNetworkData) : []), + ...(env ? [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }] : []), + ...(artifacts ? artifacts.map(mapArtifactsData) : []), + ...(other ? other.map(mapOtherData) : []), + ...(nested ? nested.map(mapHeatData) : []) + ] +}); + +function createErrorList(node, parent, deep = 0, errorList = []) { + if (node.errors) { + errorList.push(...node.errors.map((error) => ({ + errorLevel: error.level, + errorMessage: error.message, + name: node.name, + hasParent: deep > 2, + parentName: parent.name, + type: node.type, + }))); + } + if (node.children && node.children.length) { + node.children.map((child) => createErrorList(child, node, deep + 1, errorList)); + } + return errorList; +} + +const mapValidationDataToTree = validationData => { + let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {}; + return { + children: [ + { + name: 'HEAT', + expanded: true, + type: 'heat', + children: (HEAT ? HEAT.map(mapHeatData) : []) + }, + ...(artifacts ? [{ + name: 'artifacts', + expanded: true, + type: 'artifact', + children: (artifacts ? artifacts.map(mapArtifactsData) : []) + }] : []), + ...(network ? [{ + name: 'networks', + expanded: true, + type: 'network', + children: (network ? network.map(mapNetworkData) : []), + }] : []), + ...(volume ? [{ + name: 'volume', + expanded: true, + type: 'volume', + children: (volume ? volume.map(mapVolumeData) : []), + }] : []), + ...(other ? [{ + name: 'other', + expanded: true, + type: 'other', + children: (other ? other.map(mapOtherData) : []), + }] : []) + ] + }; +}; + +const toggleExpanded = (node, path) => { + let newNode = {...node}; + if (path.length === 0) { + newNode.expanded = !node.expanded; + } else { + let index = path[0]; + newNode.children = [ + ...node.children.slice(0, index), + toggleExpanded(node.children[index], path.slice(1)), + ...node.children.slice(index + 1) + ]; + } + return newNode; +}; + +const expandSelected = (node, selectedNode) => { + let shouldExpand = node.name === selectedNode; + let children = node.children && node.children.map(child => { + let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode); + shouldExpand = shouldExpand || shouldExpandChild; + return newChild; + }); + + return { + node: { + ...node, + expanded: node.expanded || shouldExpand, + children + }, + shouldExpand + }; +}; + +export default (state = {attachmentsTree: {}}, action) => { + switch (action.type) { + case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED: + let currentSoftwareProduct = action.response; + let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {}; + let errorList = createErrorList(attachmentsTree); + return { + ...state, + attachmentsTree, + errorList + }; + case actionTypes.TOGGLE_EXPANDED: + return { + ...state, + attachmentsTree: toggleExpanded(state.attachmentsTree, action.path) + }; + case actionTypes.SELECTED_NODE: + let selectedNode = action.nodeName; + return { + ...state, + attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node, + selectedNode + }; + case actionTypes.UNSELECTED_NODE: + return { + ...state, + selectedNode: undefined + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx new file mode 100644 index 0000000000..c52999ca46 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx @@ -0,0 +1,182 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import classNames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {nodeTypes, mouseActions} from './SoftwareProductAttachmentsConstants'; + +const typeToIcon = Object.freeze({ + heat: 'building-o', + volume: 'database', + network: 'cloud', + artifact: 'gear', + env: 'server', + other: 'cube' +}); + +const leftPanelWidth = 250; + +class SoftwareProductAttachmentsView extends React.Component { + + static propTypes = { + attachmentsTree: React.PropTypes.object.isRequired + }; + state = { + treeWidth: '400' + }; + + render() { + let {attachmentsTree, errorList} = this.props; + let {treeWidth} = this.state; + return ( +
      +
      +
      + { + attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind])) + } +
      +
      +
      this.onChangeTreeWidth(e)} className='software-product-attachments-separator'/> + +
      + {errorList.length ? this.renderErrorList(errorList) :
      {attachmentsTree.children ? + i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }
      } +
      +
      + ); + } + + renderNode(node, path) { + let isFolder = node.children && node.children.length > 0; + let {onSelectNode} = this.props; + return ( +
      + { +
      this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}> + { + isFolder && +
      this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}> + +
      + } + { + + + + + } + { + + onSelectNode(node.name)} className={this.getTreeTextClassName(node)}> + {node.name} + + } +
      + } + { + isFolder && + +
      + { + node.children.map((child, ind) => this.renderNode(child, [...path, ind])) + } +
      +
      + } +
      + ); + } + + createErrorList(errorList, node, parent) { + if (node.errors) { + node.errors.forEach(error => errorList.push({ + error, + name: node.name, + parentName: parent.name, + type: node.type + })); + } + if (node.children && node.children.length) { + node.children.map((child) => this.createErrorList(errorList, child, node)); + } + } + + renderErrorList(errors) { + let prevError = {}; + let {selectedNode} = this.props; + return errors.map(error => { + let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName; + prevError = error; + + return ( +
      this.selectNode(error.name)} + className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}> + + { + error.hasParent ? + i18n('{type} {name} in {parentName}: ', { + type: nodeTypes[error.type], + name: error.name, + parentName: error.parentName + }) : + i18n('{type} {name}: ', { + type: nodeTypes[error.type], + name: error.name + }) + } + + {error.errorMessage} +
      + ); + }); + } + + selectNode(currentSelectedNode) { + let {onUnselectNode, onSelectNode, selectedNode} = this.props; + if (currentSelectedNode !== selectedNode) { + onSelectNode(currentSelectedNode); + }else{ + onUnselectNode(); + } + + } + + getTreeRowClassName(name) { + let {hoveredNode, selectedNode} = this.props; + return classNames({ + 'tree-node-row': true, + 'tree-node-selected': name === hoveredNode, + 'tree-node-clicked': name === selectedNode + }); + } + + getTreeTextClassName(node) { + let {selectedNode} = this.props; + return classNames({ + 'tree-element-text': true, + 'error-status': node.errors, + 'error-status-selected': node.name === selectedNode + }); + } + + onChangeTreeWidth(e) { + if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { + let onMouseMove = (e) => { + this.setState({treeWidth: e.clientX - leftPanelWidth}); + }; + let onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } + } +} + +export default SoftwareProductAttachmentsView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js new file mode 100644 index 0000000000..3b8bc4f171 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.COMPONENT_UPDATE: + return { + ...state, + data: action.component + }; + case actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE: + return { + ...state, + qdata: action.payload.qdata || state.qdata, + qschema: action.payload.qschema || state.qschema + }; + case actionTypes.COMPONENT_DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js new file mode 100644 index 0000000000..e53b2ecafe --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js @@ -0,0 +1,129 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import {actionTypes} from './SoftwareProductComponentsConstants.js'; + +function baseUrl(softwareProductId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components`; +} + +function fetchSoftwareProductComponents(softwareProductId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); +} + +function putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, vspComponent) { + return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire`, vspComponent); +} + +function fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version){ + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire${versionQuery}`); +} + +function fetchSoftwareProductComponent(softwareProductId, vspComponentId, version){ + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}${versionQuery}`); +} + +function putSoftwareProductComponent(softwareProductId, vspComponentId, vspComponent) { + return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}`, { + name: vspComponent.name, + displayName: vspComponent.displayName, + description: vspComponent.description + }); +} + +const SoftwareProductComponentsActionHelper = { + fetchSoftwareProductComponents(dispatch, {softwareProductId, version}) { + return fetchSoftwareProductComponents(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.COMPONENTS_LIST_UPDATE, + componentsList: response.results + }); + }); + }, + + componentDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.COMPONENT_DATA_CHANGED, + deltaData + }); + }, + + + updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, componentData, qdata}) { + return Promise.all([ + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}), + SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) + ]); + }, + + updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}) { + return putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, qdata); + }, + + updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) { + return putSoftwareProductComponent(softwareProductId, vspComponentId, componentData).then(() => dispatch({ + type: actionTypes.COMPONENTS_LIST_EDIT, + component: { + id: vspComponentId, + ...componentData + } + })); + }, + + + fetchSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, version}) { + return fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version).then(response => { + dispatch({ + type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, + payload: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + } + }); + }); + }, + + fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, version}) { + return fetchSoftwareProductComponent(softwareProductId, vspComponentId, version).then(response => { + dispatch({ + type: actionTypes.COMPONENT_UPDATE, + component: response.data + }); + }); + }, + + componentQuestionnaireUpdated(dispatch, {data}) { + dispatch({ + type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE, + payload: { + qdata: data + } + }); + }, +}; + +export default SoftwareProductComponentsActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js new file mode 100644 index 0000000000..dee517e5d1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + COMPONENTS_LIST_UPDATE: null, + COMPONENTS_LIST_EDIT: null, + COMPONENT_UPDATE: null, + COMPONENT_DATA_CHANGED: null, + COMPONENT_QUESTIONNAIRE_UPDATE: null +}); + +export const storageConstants = keyMirror({ + backupType: { + ON_SITE: 'OnSite', + OFF_SITE: 'OffSite' + } +}); + +export const navigationItems = keyMirror({ + STORAGE: 'Storage', + PROCESS_DETAILS: 'Process Details', + MONITORING: 'Monitoring', + NETWORK: 'Network', + COMPUTE: 'Compute', + NETWORK: 'Network', + LOAD_BALANCING: 'High Availability & Load Balancing' +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js new file mode 100644 index 0000000000..f1c1ed1fcc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentsListView from './SoftwareProductComponentsListView.jsx'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductComponents} = softwareProduct; + let {componentsList} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isReadOnlyMode, + componentsList + }; +}; + + +const mapActionToProps = (dispatch, {version}) => { + return { + onComponentSelect: ({id: softwareProductId, componentId}) => { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); + } + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js new file mode 100644 index 0000000000..b91362a0cf --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.COMPONENTS_LIST_UPDATE: + return [...action.componentsList]; + case actionTypes.COMPONENTS_LIST_EDIT: + const indexForEdit = state.findIndex(component => component.id === action.component.id); + return [...state.slice(0, indexForEdit), action.component, ...state.slice(indexForEdit + 1)]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx new file mode 100644 index 0000000000..0c0ba0f646 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx @@ -0,0 +1,89 @@ +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'; + +const ComponentPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + displayName: React.PropTypes.string, + description: React.PropTypes.string +}); + +class SoftwareProductComponentsListView extends React.Component { + + state = { + localFilter: '' + }; + + static propTypes = { + isReadOnlyMode: React.PropTypes.bool, + componentsList: React.PropTypes.arrayOf(ComponentPropType), + onComponentSelect: React.PropTypes.func + }; + + render() { + let {componentsList = []} = this.props; + return ( +
      + { + componentsList.length > 0 && this.renderComponents() + } +
      + ); + } + + renderComponents() { + const {localFilter} = this.state; + let {isReadOnlyMode} = this.props; + + return ( + this.setState({localFilter: filter})} + isReadOnlyMode={isReadOnlyMode}> + {this.filterList().map(component => this.renderComponentsListItem(component))} + + ); + } + + renderComponentsListItem(component) { + let {id: componentId, name, displayName, description = ''} = component; + let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; + return ( + onComponentSelect({id, componentId})}> +
      +
      {i18n('Component')}
      +
      {displayName}
      +
      +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      + ); + } + + filterList() { + let {componentsList = []} = this.props; + + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return componentsList.filter(({displayName = '', description = ''}) => { + return escape(displayName).match(filter) || escape(description).match(filter); + }); + } + else { + return componentsList; + } + } +} + +export default SoftwareProductComponentsListView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js new file mode 100644 index 0000000000..7ac1c707ab --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentComputeView from './SoftwareProductComponentComputeView.jsx'; +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + let minNumberOfVMsSelectedByUser = 0; + if(qdata && qdata.compute && qdata.compute.numOfVMs){ + minNumberOfVMsSelectedByUser = qdata.compute.numOfVMs.minimum || 0; + } + + return { + qdata, + qschema, + isReadOnlyMode, + minNumberOfVMsSelectedByUser + }; +}; + +const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true}) (SoftwareProductComponentComputeView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx new file mode 100644 index 0000000000..3bad147117 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx @@ -0,0 +1,129 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + + +class SoftwareProductComponentComputeView extends React.Component { + + static propTypes = { + qdata: React.PropTypes.object, + qschema: React.PropTypes.object, + isReadOnlyMode: React.PropTypes.bool, + minNumberOfVMsSelectedByUser: React.PropTypes.number, + onQDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired + }; + + render() { + let {qdata, qschema, isReadOnlyMode, minNumberOfVMsSelectedByUser, onQDataChanged, onSubmit} = this.props; + + return ( +
      + onSubmit({qdata})} + className='component-questionnaire-validation-form' + isReadOnlyMode={isReadOnlyMode} + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema}> + +
      {i18n('VM Sizing')}
      +
      +
      +
      + +
      +
      + +
      +
      + +
      + +
      +
      +
      {i18n('Number of VMs')}
      +
      +
      +
      + +
      +
      + +
      +
      + +
      + +
      +
      + +
      {i18n('Guest OS')}
      +
      +
      +
      + +
      +
      +
      +
      + +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      + ); + } + + save(){ + return this.refs.computeValidationForm.handleFormSubmit(new Event('dummy')); + } +} + +export default SoftwareProductComponentComputeView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js new file mode 100644 index 0000000000..e4c330bec8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentsGeneralView from './SoftwareProductComponentsGeneralView.jsx'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {data: componentData = {} , qdata, qschema}} = softwareProductComponents; + + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + componentData, + qdata, + qschema, + isReadOnlyMode + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}), + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, + {softwareProductId, vspComponentId: componentId, componentData, qdata}); + } + }; + +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsGeneralView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx new file mode 100644 index 0000000000..5d11e42cd3 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx @@ -0,0 +1,186 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + + +class SoftwareProductComponentsGeneralView extends React.Component { + + render() { + let {qdata, qschema, onQDataChanged, onDataChanged, componentData: {displayName, description}, isReadOnlyMode} = this.props; + return( +
      +
      + +
      +

      {i18n('General')}

      +
      +
      + {/** disabled until backend will be ready to implement it +
      +
      + +
      {name}
      +
      +
      + + */} +
      + +
      +
      + onDataChanged({description})} + disabled={isReadOnlyMode} + value={description} + type='textarea'/> +
      +
      +
      +
      +
      + + { + qschema && + +

      {i18n('Hypervisor')}

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +

      {i18n('Image')}

      +
      +
      +
      + +
      +
      + +
      +
      + +
      + +
      +
      +

      {i18n('Recovery')}

      +
      +
      +
      + +
      + +
      +
      + + +
      +
      + +
      +
      + { + /** disabled until backend will be ready to implement it +
      +
      + +
      +
      + */ + } +
      +
      +

      {i18n('DNS Configuration')}

      +
      +
      +
      + +
      +
      +
      +
      +

      {i18n('Clone')}

      +
      +
      +
      + +
      +
      +
      +
      + + } +
      +
      + ); + } + + save() { + let {onSubmit, componentData, qdata} = this.props; + return onSubmit({componentData, qdata}); + } +} + +export default SoftwareProductComponentsGeneralView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js new file mode 100644 index 0000000000..4d4034de5b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentLoadBalancingView from './SoftwareProductComponentLoadBalancingRefView.jsx'; +import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {qdata, qschema}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + qdata, + qschema, + isReadOnlyMode + }; +}; + + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + }; + +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentLoadBalancingView); 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 new file mode 100644 index 0000000000..1aa2babc12 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + +const prefix = '/highAvailabilityAndLoadBalancing/'; + +const pointers = [ + { + key: 'failureLoadDistribution', + description: 'How is load distributed across live vms in the event of a vm/host failure? please describe' + }, + { + key: 'nkModelImplementation', + description: 'Does each VM implement the N+K model for redundancy and failure protection? Please describe.' + }, + { + key: 'architectureChoice', + description: 'What architecture is being implemented: ACTIVE-ACTIVE and/or ACTIVE-PASSIVE. ', + added: 'Will the arrangement be 1-1 or N-M? Please describe.' + }, + {key: 'slaRequirements', description: 'Specify application SLA requirements on Cloud platform.'}, + { + key: 'horizontalScaling', + description: 'Is horizontal scaling the preferred solution for HA and resiliency? Please describe.' + }, + { + key: 'loadDistributionMechanism', + description: 'Can load be distributed across VMs? If so, are special mechanisms needed to re-balance data across VMs?', + added: 'Please describe.' + } +]; + +class SoftwareProductComponentLoadBalancingView extends React.Component { + static propTypes = { + componentId: React.PropTypes.string.isRequired, + softwareProductId: React.PropTypes.string.isRequired, + qdata: React.PropTypes.object, + qschema: React.PropTypes.object, + currentSoftwareProduct: React.PropTypes.object + }; + + state = { + expanded: {} + }; + + renderTextAreaItem(item) { + return ( +
      +
      this.toggle(item.key)}> + + {i18n(item.description)} + {item.added &&
      {i18n(item.added)}
      } +
      +
      +
      +
      + +
      +
      +
      +
      + ); + } + + render() { + let {qdata, qschema, onQDataChanged, isReadOnlyMode} = this.props; + return ( +
      +
      +
      {i18n('High Availability & Load Balancing')}
      + + {pointers.map(pointer => this.renderTextAreaItem(pointer))} + +
      +
      + ); + } + + toggle(name) { + let st = this.state.expanded[name] ? true : false; + let newState = {...this.state}; + newState.expanded[name] = !st; + this.setState(newState); + } + + save() { + let {onSubmit, qdata} = this.props; + return onSubmit({qdata}); + } +} + +export default SoftwareProductComponentLoadBalancingView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js new file mode 100644 index 0000000000..ed7c5a116a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductComponentsMonitoringView from './SoftwareProductComponentsMonitoringView.jsx'; +import SoftwareProductComponentsMonitoringAction from './SoftwareProductComponentsMonitoringActionHelper.js'; + + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentVSP = {}}, softwareProductComponents: {monitoring}} = softwareProduct; + let {trapFilename, pollFilename} = monitoring; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + isReadOnlyMode, + trapFilename, + pollFilename + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onDropMibFileToUpload: (formData, type) => + SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, { + softwareProductId, + componentId, + formData, + type + }), + + onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, { + softwareProductId, + componentId, + type + }) + + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsMonitoringView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js new file mode 100644 index 0000000000..3faf571c09 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import SoftwareProductComponentsMonitoringConstants, {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; + +const UPLOAD = true; + +function baseUrl(vspId, componentId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${vspId}/components/${componentId}/monitors`; +} + +function snmpTrapUrl(vspId, componentId, isUpload) { + return `${baseUrl(vspId, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`; +} + +function snmpPollUrl(vspId, componentId, isUpload) { + return `${baseUrl(vspId, componentId)}/snmp${isUpload ? '/upload' : ''}`; +} + +let onInvalidFileSizeUpload = (dispatch) => dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: { + title: i18n('Upload Failed'), + msg: i18n('no zip file was uploaded or zip file doesn\'t exist') + } +}); + +let uploadSnmpTrapFile = (dispatch, {softwareProductId, componentId, formData}) => { + RestAPIUtil.create(snmpTrapUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({ + type: actionTypes.SNMP_TRAP_UPLOADED, data: {filename: formData.get('upload').name} + })); +}; + +let uploadSnmpPollFile = (dispatch, {softwareProductId, componentId, formData}) => { + RestAPIUtil.create(snmpPollUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({ + type: actionTypes.SNMP_POLL_UPLOADED, data: {filename: formData.get('upload').name} + })); +}; + +let deleteSnmpTrapFile = (dispatch, {softwareProductId, componentId}) => { + RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ + type: actionTypes.SNMP_TRAP_DELETED + })); +}; + +let deleteSnmpPollFile = (dispatch, {softwareProductId, componentId}) => { + RestAPIUtil.destroy(snmpPollUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({ + type: actionTypes.SNMP_POLL_DELETED + })); +}; + +const SoftwareProductComponentsMonitoringAction = { + + fetchExistingFiles(dispatch, {softwareProductId, componentId}){ + RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/snmp`).then(response => + dispatch({ + type: actionTypes.SNMP_FILES_DATA_CHANGE, + data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll} + }) + ); + }, + + uploadSnmpFile(dispatch, {softwareProductId, componentId, formData, type}){ + if (formData.get('upload').size) { + if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { + uploadSnmpTrapFile(dispatch, {softwareProductId, componentId, formData}); + } + else { + uploadSnmpPollFile(dispatch, {softwareProductId, componentId, formData}); + } + } + else { + onInvalidFileSizeUpload(dispatch); + } + }, + + deleteSnmpFile(dispatch, {softwareProductId, componentId, type}){ + if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { + deleteSnmpTrapFile(dispatch, {softwareProductId, componentId}); + } + else { + deleteSnmpPollFile(dispatch, {softwareProductId, componentId}); + } + } + +}; + +export default SoftwareProductComponentsMonitoringAction; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js new file mode 100644 index 0000000000..eeececb050 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + + SNMP_FILES_DATA_CHANGE: null, + + SNMP_TRAP_UPLOADED: null, + SNMP_POLL_UPLOADED: null, + + SNMP_TRAP_DELETED: null, + SNMP_POLL_DELETED: null +}); + +export default keyMirror({ + SNMP_TRAP: null, + SNMP_POLL: null +}); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js new file mode 100644 index 0000000000..72e0a85b10 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SNMP_FILES_DATA_CHANGE: + return { + ...state, + trapFilename: action.data.trapFilename, + pollFilename: action.data.pollFilename + }; + case actionTypes.SNMP_TRAP_UPLOADED: + return { + ...state, + trapFilename: action.data.filename + }; + case actionTypes.SNMP_POLL_UPLOADED: + return { + ...state, + pollFilename: action.data.filename + }; + case actionTypes.SNMP_TRAP_DELETED: + return { + ...state, + trapFilename: undefined + }; + case actionTypes.SNMP_POLL_DELETED: + return { + ...state, + pollFilename: undefined + }; + default: + return state; + } +}; 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 new file mode 100644 index 0000000000..ca090c5f2f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx @@ -0,0 +1,117 @@ +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 SoftwareProductComponentsMonitoringConstants from './SoftwareProductComponentsMonitoringConstants.js'; + +class SoftwareProductComponentsMonitoringView extends Component { + + static propTypes = { + isReadOnlyMode: PropTypes.bool, + trapFilename: PropTypes.string, + pollFilename: PropTypes.string, + softwareProductId: PropTypes.string, + + onDropMibFileToUpload: PropTypes.func, + onDeleteSnmpFile: PropTypes.func + }; + + state = { + dragging: false + }; + + + render() { + return ( +
      + {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_TRAP)} + {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_POLL)} +
      + ); + } + + renderDropzoneWithType(type) { + let {isReadOnlyMode, trapFilename, pollFilename} = this.props; + let fileName; + if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) { + fileName = trapFilename; + } + else { + fileName = pollFilename; + } + let refAndName = `fileInput${type.toString()}`; + let typeDisplayName = this.getFileTypeDisplayName(type); + return ( + this.handleImport(files, {isReadOnlyMode, type, refAndName})} + onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref={refAndName} + name={refAndName} + accept='.zip' + disabled> +
      +
      {typeDisplayName}
      + {fileName ? this.renderUploadedFileName(fileName, type) : this.renderUploadButton(refAndName)} +
      +
      + ); + } + + renderUploadButton(refAndName) { + let {isReadOnlyMode} = this.props; + return ( +
      +
      {i18n('Drag & drop for upload')}
      +
      {i18n('or')}
      +
      this.refs[refAndName].open()}> + {i18n('Select file')} +
      +
      + ); + } + + renderUploadedFileName(filename, type) { + return ( + + + + + + + ); + } + + + handleOnDragEnter(isReadOnlyMode) { + if (!isReadOnlyMode) { + this.setState({dragging: true}); + } + } + + handleImport(files, {isReadOnlyMode, type, refAndName}) { + if (isReadOnlyMode) { + return; + } + + this.setState({dragging: false}); + let file = files[0]; + let formData = new FormData(); + formData.append('upload', file); + this.refs[refAndName].value = ''; + this.props.onDropMibFileToUpload(formData, type); + } + + getFileTypeDisplayName(type) { + return type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP ? 'SNMP Trap' : 'SNMP Poll'; + } + +} + +export default SoftwareProductComponentsMonitoringView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js new file mode 100644 index 0000000000..a412456e13 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; +import SoftwareProductComponentsNICEditorView from './SoftwareProductComponentsNICEditorView.jsx'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + + let {network: {nicEditor = {}}} = softwareProductComponents; + let {data, qdata, qschema} = nicEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isValidityData, + data, + qdata, + qschema, + isReadOnlyMode + }; + +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onDataChanged: deltaData => SoftwareProductComponentsNetworkActionHelper.updateNICData(dispatch, {deltaData}), + onSubmit: ({data, qdata}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}), + onCancel: () => SoftwareProductComponentsNetworkActionHelper.closeNICEditor(dispatch), + onQDataChanged: ({data}) => SoftwareProductComponentsNetworkActionHelper.updateNICQuestionnaire(dispatch, {data}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentsNICEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js new file mode 100644 index 0000000000..d49f9ccb1e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.NICEditor.OPEN: + return { + ...state, + data: action.nic + }; + case actionTypes.NICEditor.CLOSE: + return {}; + case actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE: + return { + ...state, + qdata: action.payload.qdata || state.qdata, + qschema: action.payload.qschema || state.qschema + }; + case actionTypes.NICEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx new file mode 100644 index 0000000000..7393a835dc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx @@ -0,0 +1,322 @@ +import React from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +class SoftwareProductComponentsNetworkEditorView extends React.Component { + + render() { + let {onCancel, isReadOnlyMode} = this.props; + return ( + this.submit() } + onReset={ () => onCancel() } + labledButtons={true} + isReadOnlyMode={isReadOnlyMode} + className='vsp-components-network-editor'> + {this.renderEditorFields()} + + ); + } + + renderEditorFields() { + let {data = {}, qdata = {}, qschema = {}, onQDataChanged, onDataChanged, isReadOnlyMode} = this.props; + let {name, description, networkName} = data; + let netWorkValues = [{ + enum: networkName, + title: networkName + }]; + return( +
      +
      +
      + +
      +
      + onDataChanged({description})} + disabled={isReadOnlyMode} + type='textarea'/> +
      +
      + +
      +
      {i18n('Protocols')}
      +
      + +
      +
      + +
      +
      +
      +
      {i18n('IP Configuration')}
      +
      + +
      +
      + +
      +
      +
      +
      +
      {i18n('Network')}
      +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      {i18n('Sizing')}
      +
      + +
      +
      + +
      +
      {i18n('Inflow Traffic per second')}
      +
      + +
      +
      +
      +
      {i18n('Packets')}
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      {i18n('Bytes')}
      +
      +
      +
      + + +
      +
      + +
      +
      +
      +
      + +
      +
      {i18n('Outflow Traffic per second')}
      +
      + +
      +
      +
      +
      {i18n('Packets')}
      +
      +
      +
      + +
      +
      + + +
      +
      +
      +
      +
      +
      {i18n('Bytes')}
      +
      +
      +
      + + +
      +
      + + +
      +
      +
      +
      + +
      +
      {i18n('Flow Length')}
      +
      + +
      +
      +
      +
      {i18n('Packets')}
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      {i18n('Bytes')}
      +
      +
      +
      + + +
      +
      + +
      +
      +
      +
      + +
      +
      +
      +
      {i18n('Acceptable Jitter')}
      +
      +
      +
      + +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      {i18n('Acceptable Packet Loss %')}
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      + + ); + } + submit() { + let {data, qdata, onSubmit} = this.props; + onSubmit({data, qdata}); + } +} + +export default SoftwareProductComponentsNetworkEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js new file mode 100644 index 0000000000..bc53e1a7af --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.NIC_LIST_UPDATE: + return [...action.response]; + case actionTypes.NIC_LIST_EDIT: + const indexForEdit = state.findIndex(nic => nic.id === action.nic.id); + return [...state.slice(0, indexForEdit), action.nic, ...state.slice(indexForEdit + 1)]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js new file mode 100644 index 0000000000..8ff6b50189 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js @@ -0,0 +1,129 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js'; + +function baseUrl(softwareProductId, componentId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics`; +} + + +function fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire${versionQuery}`); +} + +function fetchNIC({softwareProductId, componentId, nicId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}${versionQuery}`); +} + +function fetchNICsList({softwareProductId, componentId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +} + +function saveNIC({softwareProductId, componentId, nic: {id, name, description, networkId}}) { + return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${id}`,{ + name, + description, + networkId + }); +} + +function saveNICQuestionnaire({softwareProductId, componentId, nicId, qdata}) { + return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire`, qdata); +} + +const SoftwareProductComponentNetworkActionHelper = { + + fetchNICsList(dispatch, {softwareProductId, componentId, version}) { + return fetchNICsList({softwareProductId, componentId, version}).then((response) => { + dispatch({ + type: actionTypes.NIC_LIST_UPDATE, + response: response.results + }); + }); + }, + + openNICEditor(dispatch, {nic = {}, data = {}}) { + dispatch({ + type: actionTypes.NICEditor.OPEN, + nic: {...data, id: nic.id} + }); + }, + + closeNICEditor(dispatch) { + dispatch({ + type: actionTypes.NICEditor.CLOSE + }); + }, + + loadNICData({softwareProductId, componentId, nicId, version}) { + return fetchNIC({softwareProductId, componentId, nicId, version}); + }, + + loadNICQuestionnaire(dispatch, {softwareProductId, componentId, nicId, version}) { + return fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}).then((response) => { + dispatch({ + type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, + payload: { + qdata: response.data ? JSON.parse(response.data) : {}, + qschema: JSON.parse(response.schema) + } + }); + }); + }, + + updateNICData(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.NICEditor.DATA_CHANGED, + deltaData + }); + }, + + updateNICQuestionnaire(dispatch, {data}) { + dispatch({ + type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE, + payload: { + qdata: data + } + }); + }, + + saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}) { + SoftwareProductComponentNetworkActionHelper.closeNICEditor(dispatch); + return Promise.all([ + saveNICQuestionnaire({softwareProductId, componentId, nicId: data.id, qdata}), + saveNIC({softwareProductId, componentId, nic: data}).then(() => { + dispatch({ + type: actionTypes.NIC_LIST_EDIT, + nic: data + }); + }) + ]); + } +}; + +export default SoftwareProductComponentNetworkActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js new file mode 100644 index 0000000000..193f4b20b5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + NIC_LIST_EDIT: null, + NIC_LIST_UPDATE: null, + + NICEditor: { + OPEN: null, + CLOSE: null, + NIC_QUESTIONNAIRE_UPDATE: null, + DATA_CHANGED: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js new file mode 100644 index 0000000000..9172dc691a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js @@ -0,0 +1,86 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductComponentsNetworkListView from './SoftwareProductComponentsNetworkListView.jsx'; +import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js'; + + +export const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct; + let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, qschema}} = softwareProductComponents; + let {data} = nicEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {version} = currentSoftwareProduct; + let manualMode = nicList.length <= 0; + let isModalInEditMode = true; + + return { + version, + componentData, + qdata, + qschema, + isValidityData, + nicList, + isDisplayModal: Boolean(data), + isModalInEditMode, + manualMode, + isReadOnlyMode + }; + +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onAddNIC: () => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch), + onEditNicClick: (nic, version) => { + Promise.all([ + SoftwareProductComponentsNetworkActionHelper.loadNICData({ + softwareProductId, + componentId, + nicId: nic.id, + version + }), + SoftwareProductComponentsNetworkActionHelper.loadNICQuestionnaire(dispatch, { + softwareProductId, + componentId, + nicId: nic.id, + version + }) + ]).then( + ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data}) + ); + }, + onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, + {softwareProductId, + vspComponentId: componentId, + qdata}); + } + + + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsNetworkListView); 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 new file mode 100644 index 0000000000..b3e17ff94b --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx @@ -0,0 +1,136 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js'; + +class SoftwareProductComponentsNetworkView extends React.Component { + + state = { + localFilter: '' + }; + + render() { + let {qdata, qschema, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props; + + return( +
      +
      +
      + +

      {i18n('Network Capacity')}

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      + + +
      + {this.renderNicList()} +
      + + + {isModalInEditMode ? i18n('Edit NIC') : i18n('Create New NIC')} + + + { + + } + + +
      + ); + } + + renderNicList() { + const {localFilter} = this.state; + let {onAddNIC, manualMode, isReadOnlyMode} = this.props; + let onAdd = manualMode ? onAddNIC : false; + return ( + this.setState({localFilter: filter})}> + {!manualMode && this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))} + + ); + } + + renderNicListItem(nic, isReadOnlyMode) { + let {id, name, description, networkName = ''} = nic; + let {onEditNicClick, version} = this.props; + return ( + onEditNicClick(nic, version)}> + +
      +
      {i18n('Name')}
      +
      {name}
      +
      +
      +
      {i18n('Purpose of NIC')}
      +
      {description}
      +
      +
      +
      {i18n('Network')}
      +
      {networkName}
      +
      + +
      + ); + } + + filterList() { + let {nicList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return nicList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return nicList; + } + } + + save() { + let {onSubmit, qdata} = this.props; + return onSubmit({qdata}); + } +} + +export default SoftwareProductComponentsNetworkView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js new file mode 100644 index 0000000000..d535a34a82 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js @@ -0,0 +1,145 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; + +function baseUrl(softwareProductId, componentId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`; +} + +function fetchProcessesList({softwareProductId, componentId, version}) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`); +} + +function deleteProcess({softwareProductId, componentId, processId}) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId)}/${processId}`); +} + +function putProcess({softwareProductId, componentId, process}) { + return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${process.id}`, { + name: process.name, + description: process.description + }); +} + +function postProcess({softwareProductId,componentId, process}) { + return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}`, { + name: process.name, + description: process.description + }); +} + +function uploadFileToProcess({softwareProductId, processId, componentId, formData}) { + return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}/${processId}/upload`, formData); +} + + + +const SoftwareProductComponentProcessesActionHelper = { + fetchProcessesList(dispatch, {softwareProductId, componentId, version}) { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, + processesList: [] + }); + + return fetchProcessesList({softwareProductId, componentId, version}).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES, + processesList: response.results + }); + }); + }, + + deleteProcess(dispatch, {process, softwareProductId, componentId}) { + return deleteProcess({softwareProductId, processId:process.id, componentId}).then(() => { + dispatch({ + type: actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, + processId: process.id + }); + }); + + }, + + saveProcess(dispatch, {softwareProductId, componentId, previousProcess, process}) { + if (previousProcess) { + return putProcess({softwareProductId,componentId, process}).then(() => { + if (process.formData && process.formData.name !== previousProcess.artifactName){ + uploadFileToProcess({softwareProductId, processId: process.id, formData: process.formData, componentId}); + } + dispatch({ + type: actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, + process + }); + }); + } + else { + return postProcess({softwareProductId, componentId, process}).then(response => { + if (process.formData) { + uploadFileToProcess({softwareProductId, processId: response.value, formData: process.formData, componentId}); + } + dispatch({ + type: actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS, + process: { + ...process, + id: response.value + } + }); + }); + } + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM, + processToDelete: false + }); + }, + + openDeleteProcessesConfirm(dispatch, {process} ) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM, + processToDelete: process + }); + }, + + openEditor(dispatch, process = {}) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN, + process + }); + }, + closeEditor(dispatch) { + dispatch({ + type:actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE + }); + }, + processEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.processEditor.DATA_CHANGED, + deltaData + }); + } +}; + +export default SoftwareProductComponentProcessesActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js new file mode 100644 index 0000000000..78a111a426 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null, + EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null, + DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null, + SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: null, + SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: null, + FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES: null, + SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM: null, + processEditor: { + DATA_CHANGED: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js new file mode 100644 index 0000000000..0138023c30 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper'; +import SoftwareProductComponentProcessesEditorView from './SoftwareProductComponentProcessesEditorView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductComponents: {componentProcesses = {}}} = softwareProduct; + let {processesList = [], processesEditor = {}} = componentProcesses; + let {data} = processesEditor; + + let previousData; + const processId = data ? data.id : null; + if(processId) { + previousData = processesList.find(process => process.id === processId); + } + + return { + data, + previousData + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => { + + return { + onDataChanged: deltaData => SoftwareProductComponentProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onCancel: () => SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch), + onSubmit: ({previousProcess, process}) => { + SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch); + SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, componentId, process}); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentProcessesEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js new file mode 100644 index 0000000000..f859f690e8 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: + return { + ...state, + data: action.process + }; + case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: + return {}; + + case actionTypes.processEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; 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 new file mode 100644 index 0000000000..ca6d843af7 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx @@ -0,0 +1,124 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Dropzone from 'react-dropzone'; + + +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + artifactName: React.PropTypes.string +}); + +class SoftwareProductProcessesEditorView extends React.Component { + + state = { + dragging: false, + files: [] + }; + + static propTypes = { + data: SoftwareProductProcessEditorPropType, + previousData: SoftwareProductProcessEditorPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func, + onSubmit: React.PropTypes.func, + onCancel: React.PropTypes.func + }; + + render() { + let {isReadOnlyMode, onCancel, onDataChanged, data = {}} = this.props; + let {name, description, artifactName} = data; + + return ( +
      + this.submit() } + onReset={ () => onCancel() } + className='vsp-processes-editor'> +
      + this.handleImportSubmit(files)} + onDragEnter={() => this.setState({dragging:true})} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput' + accept='*.*'> +
      +
      + onDataChanged({name})} + label={i18n('Name')} + value={name} + validations={{validateName: true, maxLength: 120, required: true}} + type='text'/> + +
      +
      +
      +
      {i18n('Drag & drop for upload')}
      +
      {i18n('or')}
      +
      this.refs.processEditorFileInput.open()}> + {i18n('Select file')} +
      +
      +
      +
      + onDataChanged({description})} + label={i18n('Notes')} + value={description} + name='vsp-process-description' + className='vsp-process-description' + validations={{maxLength: 1000}} + type='textarea'/> +
      +
      +
      +
      + ); + } + + submit() { + const {data: process, previousData: previousProcess} = this.props; + let {files} = this.state; + let formData = new FormData(); + if (files.length) { + let file = files[0]; + formData.append('upload', file); + } + + let updatedProcess = { + ...process, + formData: files.length ? formData : false + }; + this.props.onSubmit({process: updatedProcess, previousProcess}); + } + + + handleImportSubmit(files) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } +} + +export default SoftwareProductProcessesEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js new file mode 100644 index 0000000000..5f6932897e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; + +import SoftwareProductComponentsProcessesListView from './SoftwareProductComponentsProcessesListView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + + let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents: {componentProcesses = {}}} = softwareProduct; + let{processesList = [], processesEditor = {}} = componentProcesses; + let {data} = processesEditor; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + isValidityData, + processesList, + isDisplayModal: Boolean(data), + isModalInEditMode: Boolean(data && data.id), + isReadOnlyMode + }; + +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + + return { + onAddProcess: () => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch), + onEditProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch, process), + onDeleteProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsProcessesListView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js new file mode 100644 index 0000000000..4bb124d52f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductComponentProcessesConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES: + return [...action.processesList]; + case actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: + const indexForEdit = state.findIndex(process => process.id === action.process.id); + return [...state.slice(0, indexForEdit), action.process, ...state.slice(indexForEdit + 1)]; + case actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: + return [...state, action.process]; + case actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: + return state.filter(process => process.id !== action.processId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx new file mode 100644 index 0000000000..48fa862364 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function renderMsg(processToDelete) { + let name = processToDelete ? processToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + return ( +
      +

      {msg}

      +
      + ); +}; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor, softwareProductComponents} = softwareProduct; + let {componentProcesses} = softwareProductComponents; + let {processToDelete} = componentProcesses; + let softwareProductId = softwareProductEditor.data.id; + const show = processToDelete !== false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: renderMsg(processToDelete), + confirmationDetails: {processToDelete, softwareProductId} + }; +}; + +const mapActionsToProps = (dispatch,{componentId, softwareProductId}) => { + return { + onConfirmed: ({processToDelete}) => { + SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId, componentId}); + SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx new file mode 100644 index 0000000000..a8b07e9194 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx @@ -0,0 +1,125 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import SoftwareProductProcessesEditor from './SoftwareProductComponentProcessesEditor.js'; +import SoftwareProductComponentsProcessesConfirmationModal from './SoftwareProductComponentsProcessesConfirmationModal.jsx'; + +class SoftwareProductProcessesView extends React.Component { + + state = { + localFilter: '' + }; + + static propTypes = { + onAddProcess: React.PropTypes.func, + onEditProcessClick: React.PropTypes.func, + onDeleteProcessClick: React.PropTypes.func, + isDisplayModal: React.PropTypes.bool, + isModalInEditMode: React.PropTypes.bool, + onStorageSelect: React.PropTypes.func, + componentId: React.PropTypes.string, + softwareProductId: React.PropTypes.string + }; + + render() { + let { softwareProductId, componentId} = this.props; + + return ( +
      +
      +
      + {this.renderEditor()} + {this.renderProcessList()} +
      + +
      +
      + ); + } + + renderEditor() { + let {softwareProductId, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props; + return ( + + + {isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')} + + + + + + + ); + } + + renderProcessList() { + const {localFilter} = this.state; + let {onAddProcess, isReadOnlyMode} = this.props; + return ( +
      + this.setState({localFilter: filter})}> + {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} + +
      + ); + } + + renderProcessListItem(process, isReadOnlyMode) { + let {id, name, description, artifactName = ''} = process; + let {onEditProcessClick, onDeleteProcessClick} = this.props; + return ( + onEditProcessClick(process)} + onDelete={() => onDeleteProcessClick(process)}> + +
      +
      {i18n('Name')}
      +
      {name}
      +
      +
      +
      {i18n('Artifact name')}
      +
      {artifactName}
      +
      +
      +
      {i18n('Notes')}
      +
      {description}
      +
      +
      + ); + } + + + filterList() { + let {processesList} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return processesList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return processesList; + } + } +} + +export default SoftwareProductProcessesView; 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 new file mode 100644 index 0000000000..fbd3f81ec2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; +import SoftwareProductComponentStorageView from './SoftwareProductComponentStorageView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct; + let {componentEditor: {data: componentData , qdata, qschema}} = softwareProductComponents; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP); + + return { + componentData, + qdata, + qschema, + isReadOnlyMode + }; +}; + +const mapActionToProps = (dispatch, {softwareProductId, componentId}) => { + return { + onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}), + onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});} + }; +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true}) (SoftwareProductComponentStorageView); 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 new file mode 100644 index 0000000000..9c9600c376 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx @@ -0,0 +1,124 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx'; + + +class SoftwareProductComponentStorageView extends React.Component { + + static propTypes = { + componentId: React.PropTypes.string, + onQDataChanged: React.PropTypes.func, + onSubmit: React.PropTypes.func, + isReadOnlyMode: React.PropTypes.bool + }; + + render() { + let {qdata, qschema, onQDataChanged, onSubmit, isReadOnlyMode} = this.props; + + return( +
      + onSubmit({qdata})} + className='component-questionnaire-validation-form' + isReadOnlyMode={isReadOnlyMode} + onDataChanged={onQDataChanged} + data={qdata} + schema={qschema}> + +
      {i18n('Backup')}
      +
      +
      +
      +
      + +
      + +
      +
      +
      +
      + +
      +
      + +
      + +
      +
      + +
      {i18n('Snapshot Backup')}
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      {i18n('Log Backup')}
      +
      +
      +
      + +
      +
      + +
      +
      + +
      + +
      +
      + +
      + ); + } + + save(){ + return this.refs.storageValidationForm.handleFormSubmit(new Event('dummy')); + } +} + +export default SoftwareProductComponentStorageView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js new file mode 100644 index 0000000000..46308f0045 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import SoftwareProductCreationActionHelper from './SoftwareProductCreationActionHelper.js'; +import SoftwareProductCreationView from './SoftwareProductCreationView.jsx'; + +const mapStateToProps = ({finalizedLicenseModelList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => { + return { + data: softwareProductCreation.data, + softwareProductCategories, + finalizedLicenseModelList + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => SoftwareProductCreationActionHelper.changeData(dispatch, {deltaData}), + onCancel: () => SoftwareProductCreationActionHelper.resetData(dispatch), + onSubmit: (softwareProduct) => { + SoftwareProductCreationActionHelper.resetData(dispatch); + SoftwareProductCreationActionHelper.createSoftwareProduct(dispatch, {softwareProduct}).then(softwareProductId => { + let {vendorId: licenseModelId, licensingVersion} = softwareProduct; + OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion}); + }); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductCreationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js new file mode 100644 index 0000000000..f4e51f198e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js @@ -0,0 +1,77 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import {actionTypes} from './SoftwareProductCreationConstants.js'; + + +function baseUrl() { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/`; +} + +function createSoftwareProduct(softwareProduct) { + return RestAPIUtil.create(baseUrl(), { + ...softwareProduct, + icon: 'icon', + licensingData: {} + }); +} + +const SoftwareProductCreationActionHelper = { + + open(dispatch) { + SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch); + dispatch({ + type: actionTypes.OPEN + }); + }, + + resetData(dispatch) { + dispatch({ + type: actionTypes.RESET_DATA + }); + }, + + changeData(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.DATA_CHANGED, + deltaData + }); + }, + + createSoftwareProduct(dispatch, {softwareProduct}) { + return createSoftwareProduct(softwareProduct).then(response => { + SoftwareProductActionHelper.addSoftwareProduct(dispatch, { + softwareProduct: { + ...softwareProduct, + id: response.vspId + } + }); + return response.vspId; + }); + } + +}; + +export default SoftwareProductCreationActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js new file mode 100644 index 0000000000..0a9cdb911c --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + OPEN: null, + RESET_DATA: null, + DATA_CHANGED: null +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js new file mode 100644 index 0000000000..5e3db09e56 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductCreationConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.OPEN: + return { + ...state, + data: {}, + showModal: true + }; + case actionTypes.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.RESET_DATA: + return {}; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx new file mode 100644 index 0000000000..2c8f243457 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx @@ -0,0 +1,123 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; + +import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; + + +const SoftwareProductPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + category: React.PropTypes.string, + subCategory: React.PropTypes.string, + vendorId: React.PropTypes.string +}); + +class SoftwareProductCreationView extends React.Component { + + static propTypes = { + data: SoftwareProductPropType, + finalizedLicenseModelList: React.PropTypes.array, + softwareProductCategories: React.PropTypes.array, + onDataChanged: React.PropTypes.func.isRequired, + onSubmit: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + render() { + let {softwareProductCategories, data = {}, onDataChanged, onCancel} = this.props; + let {name, description, vendorId, subCategory} = data; + + const vendorList = this.getVendorList(); + + return ( +
      + this.submit() } + onReset={() => onCancel() } + labledButtons={true}> +
      +
      + onDataChanged({name})} + validations={{validateName: true, maxLength: 25, required: true}} + type='text' + className='field-section'/> + onDataChanged({vendorId})} + value={vendorId} + label={i18n('Vendor')} + values={vendorList} + validations={{required: true}} + type='select' + className='field-section'/> + this.onSelectSubCategory(subCategory)} + validations={{required: true}} + className='options-input-category'> + + {softwareProductCategories.map(category => + category.subcategories && + {category.subcategories.map(sub => + )} + ) + } + +
      +
      + onDataChanged({description})} + validations={{freeEnglishText: true, maxLength: 1000, required: true}} + type='textarea' + className='field-section'/> +
      +
      +
      +
      + ); + } + + getVendorList() { + let {finalizedLicenseModelList} = this.props; + + return [{enum: '', title: i18n('please select...')}].concat(finalizedLicenseModelList.map(vendor => { + return { + enum: vendor.id, + title: vendor.vendorName + }; + })); + } + + onSelectSubCategory(subCategory) { + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory}); + } + + create(){ + this.refs.validationForm.handleFormSubmit(new Event('dummy')); + } + + submit() { + const {data:softwareProduct, finalizedLicenseModelList} = this.props; + softwareProduct.vendorName = finalizedLicenseModelList.find(vendor => vendor.id === softwareProduct.vendorId).vendorName; + this.props.onSubmit(softwareProduct); + } +} + +export default SoftwareProductCreationView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js new file mode 100644 index 0000000000..16a100c664 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import SoftwareProductDetailsView from './SoftwareProductDetailsView.jsx'; + +export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, licenseModel: {licenseAgreement, featureGroup}}) => { + let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct; + let {licensingData = {}, licensingVersion} = currentSoftwareProduct; + let licenseAgreementList = [], filteredFeatureGroupsList = []; + if(licensingVersion && licensingVersion !== '') { + licenseAgreementList = licenseAgreement.licenseAgreementList; + let selectedLicenseAgreement = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); + if (selectedLicenseAgreement) { + let featureGroupsList = featureGroup.featureGroupsList.filter(({referencingLicenseAgreements}) => referencingLicenseAgreements.includes(selectedLicenseAgreement.id)); + if (featureGroupsList.length) { + filteredFeatureGroupsList = featureGroupsList.map(featureGroup => ({enum: featureGroup.id, title: featureGroup.name})); + } + } + } + let {qdata, qschema} = softwareProductQuestionnaire; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct, + softwareProductCategories, + licenseAgreementList, + featureGroupsList: filteredFeatureGroupsList, + finalizedLicenseModelList, + qdata, + qschema, + isReadOnlyMode + }; +}; + +export const mapActionsToProps = (dispatch) => { + return { + onDataChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}), + onVendorParamChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData}), + onQDataChanged: ({data}) => SoftwareProductActionHelper.softwareProductQuestionnaireUpdate(dispatch, {data}), + onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}), + onSubmit: (softwareProduct, qdata) =>{ return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});} + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductDetailsView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js new file mode 100644 index 0000000000..e060706b37 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.softwareProductEditor.OPEN: + return { + ...state, + data: {} + }; + case actionTypes.softwareProductEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + case actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION: + return { + ...state, + uploadData:action.uploadData + }; + case actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED: + return { + ...state, + isValidityData: action.isValidityData + }; + case actionTypes.softwareProductEditor.CLOSE: + return {}; + case actionTypes.SOFTWARE_PRODUCT_LOADED: + return { + ...state, + data: action.response + }; + case actionTypes.TOGGLE_NAVIGATION_ITEM: + return { + ...state, + mapOfExpandedIds: action.mapOfExpandedIds + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx new file mode 100644 index 0000000000..75a5797dec --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx @@ -0,0 +1,264 @@ +import React, {Component, PropTypes} from 'react'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Form from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; +import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; + +class SoftwareProductDetails extends Component { + + static propTypes = { + vendorName: PropTypes.string, + currentSoftwareProduct: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + description: PropTypes.string, + category: PropTypes.string, + subCategory: PropTypes.string, + vendorId: PropTypes.string, + vendorName: PropTypes.string, + licensingVersion: PropTypes.string, + licensingData: PropTypes.shape({ + licenceAgreement: PropTypes.string, + featureGroups: PropTypes.array + }) + }), + softwareProductCategories: PropTypes.array, + finalizedLicenseModelList: PropTypes.array, + licenseAgreementList: PropTypes.array, + featureGroupsList: PropTypes.array, + onSubmit: PropTypes.func.isRequired, + onDataChanged: PropTypes.func.isRequired, + onValidityChanged: PropTypes.func.isRequired, + qdata: PropTypes.object.isRequired, + qschema: PropTypes.object.isRequired, + onQDataChanged: PropTypes.func.isRequired, + onVendorParamChanged: PropTypes.func.isRequired + }; + + state = { + licensingVersionsList: [] + }; + + render() { + let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, featureGroupsList, licenseAgreementList, currentSoftwareProduct} = this.props; + let {name, description, vendorId, licensingVersion, subCategory, licensingData = {}} = currentSoftwareProduct; + let licensingVersionsList = this.state.licensingVersionsList.length > 0 ? this.state.licensingVersionsList : this.refreshVendorVersionsList(vendorId); + let {qdata, qschema, onQDataChanged} = this.props; + let {isReadOnlyMode} = this.props; + + return ( +
      +
      this.props.onSubmit(currentSoftwareProduct, qdata)} + onValidityChanged={(isValidityData) => this.props.onValidityChanged(isValidityData)} + isReadOnlyMode={isReadOnlyMode}> +
      {i18n('General')}
      +
      +
      + onDataChanged({name})} + validations={{validateName: true, maxLength: 120, required: true}} + className='field-section'/> + this.onVendorParamChanged({vendorId})} + className='field-section'> + {finalizedLicenseModelList.map(lm => )} + +
      + this.onSelectSubCategory(subCategory)} + className='field-section'> + { + softwareProductCategories.map(category => + category.subcategories && + {category.subcategories.map(sub => + )} + + ) + } + +
      +
      +
      + onDataChanged({description})} + className='field-section' + validations={{required: true}}/> +
      +
      +
      +
      {i18n('Licenses')}
      +
      + this.onVendorParamChanged({vendorId, licensingVersion})} + selectedEnum={licensingVersion} + label={i18n('Licensing Version')} + values={licensingVersionsList} + type='select' + className='field-section'/> + this.onLicensingDataChanged({licenseAgreement, featureGroups: []})}> + + {licenseAgreementList.map(la => )} + +
      +
      + {licensingData.licenseAgreement && ( + this.onFeatureGroupsChanged({featureGroups})} + multiSelectedEnum={licensingData.featureGroups} + name='feature-groups' + label={i18n('Feature Groups')} + clearable={false} + values={featureGroupsList}/>) + } +
      +
      +
      +
      +
      +
      {i18n('Availability')}
      +
      +
      + +
      +
      +
      {i18n('Regions')}
      +
      +
      + +
      +
      +
      {i18n('Storage Data Replication')}
      +
      +
      + + +
      +
      + + +
      +
      +
      +
      +
      + ); + } + + onVendorParamChanged({vendorId, licensingVersion}) { + let {finalizedLicenseModelList, onVendorParamChanged} = this.props; + if(!licensingVersion) { + const licensingVersionsList = this.refreshVendorVersionsList(vendorId); + licensingVersion = licensingVersionsList.length > 0 ? licensingVersionsList[0].enum : ''; + } + let vendorName = finalizedLicenseModelList.find(licenseModelItem => licenseModelItem.id === vendorId).vendorName || ''; + let deltaData = { + vendorId, + vendorName, + licensingVersion, + licensingData: {} + }; + onVendorParamChanged(deltaData); + } + + refreshVendorVersionsList(vendorId) { + if(!vendorId) { + return []; + } + + let {finalVersions} = this.props.finalizedLicenseModelList.find(vendor => vendor.id === vendorId); + + let licensingVersionsList = [{ + enum: '', + title: i18n('Select...') + }]; + if(finalVersions) { + finalVersions.forEach(version => licensingVersionsList.push({ + enum: version, + title: version + })); + } + + return licensingVersionsList; + } + + onSelectSubCategory(subCategory) { + let {softwareProductCategories, onDataChanged} = this.props; + let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories); + onDataChanged({category, subCategory}); + } + + onFeatureGroupsChanged({featureGroups}) { + this.onLicensingDataChanged({featureGroups}); + } + + onLicensingDataChanged(deltaData) { + this.props.onDataChanged({ + licensingData: { + ...this.props.currentSoftwareProduct.licensingData, + ...deltaData + } + }); + } + + save(){ + return this.refs.validationForm.handleFormSubmit(new Event('dummy')); + } +} + +export default SoftwareProductDetails; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js new file mode 100644 index 0000000000..7604f5841d --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js @@ -0,0 +1,97 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import LandingPageView from './SoftwareProductLandingPageView.jsx'; + +const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => { + let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct; + let {licensingData = {}} = currentSoftwareProduct; + let {licenseAgreementList} = licenseAgreement; + let {componentsList} = softwareProductComponents; + let licenseAgreementName = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement); + if (licenseAgreementName) { + licenseAgreementName = licenseAgreementName.name; + } + + let categoryName = '', subCategoryName = '', fullCategoryDisplayName = ''; + const category = softwareProductCategories.find(ca => ca.uniqueId === currentSoftwareProduct.category); + if (category) { + categoryName = category.name; + const subcategories = category.subcategories || []; + const subcat = subcategories.find(sc => sc.uniqueId === currentSoftwareProduct.subCategory); + subCategoryName = subcat && subcat.name ? subcat.name : ''; + } + fullCategoryDisplayName = `${subCategoryName} (${categoryName})`; + + const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + + return { + currentSoftwareProduct: { + ...currentSoftwareProduct, + licenseAgreementName, + fullCategoryDisplayName + }, + isReadOnlyMode, + componentsList + }; +}; + +const mapActionsToProps = (dispatch, {version}) => { + return { + onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, { + softwareProductId, + licenseModelId + }), + onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}), + onUpload: (softwareProductId, formData) => + SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed') + }), + onUploadConfirmation: (softwareProductId, formData) => + SoftwareProductActionHelper.uploadConfirmation(dispatch, { + softwareProductId, + formData, + failedNotificationTitle: i18n('Upload validation failed')}), + + onInvalidFileSizeUpload: () => dispatch({ + type: NotificationConstants.NOTIFY_ERROR, + data: { + title: i18n('Upload Failed'), + msg: i18n('no zip file was uploaded or zip file doesn\'t exist') + } + }), + onComponentSelect: ({id: softwareProductId, componentId}) => { + OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version }); + }, + /** for the next version */ + onAddComponent: () => SoftwareProductActionHelper.addComponent(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(LandingPageView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx new file mode 100644 index 0000000000..4a848834b2 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx @@ -0,0 +1,38 @@ +import {connect} from 'react-redux'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor} = softwareProduct; + let {uploadData} = softwareProductEditor; + const show = uploadData ? true : false; + return { + show, + title: 'Warning!', + type: 'warning', + msg: i18n('Upload will erase existing data. Do you want to continue?'), + confirmationDetails: {uploadData} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({uploadData}) => { + let {softwareProductId, formData, failedNotificationTitle} = uploadData; + SoftwareProductActionHelper.uploadFile(dispatch, { + softwareProductId, + formData, + failedNotificationTitle + }); + SoftwareProductActionHelper.hideUploadConfirm(dispatch); + }, + onDeclined: () => { + SoftwareProductActionHelper.hideUploadConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx new file mode 100644 index 0000000000..cf7c7a31a5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx @@ -0,0 +1,272 @@ +import React from 'react'; +import classnames from 'classnames'; +import Dropzone from 'react-dropzone'; + + +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 FontAwesome from 'react-fontawesome'; +import SoftwareProductLandingPageUploadConfirmationModal from './SoftwareProductLandingPageUploadConfirmationModal.jsx'; + + +const SoftwareProductPropType = React.PropTypes.shape({ + name: React.PropTypes.string, + description: React.PropTypes.string, + version: React.PropTypes.string, + id: React.PropTypes.string, + categoryId: React.PropTypes.string, + vendorId: React.PropTypes.string, + status: React.PropTypes.string, + licensingData: React.PropTypes.object, + validationData: React.PropTypes.object +}); + +const ComponentPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + displayName: React.PropTypes.string, + description: React.PropTypes.string +}); + +class SoftwareProductLandingPageView extends React.Component { + + state = { + localFilter: '', + fileName: '', + dragging: false, + files: [] + }; + + static propTypes = { + currentSoftwareProduct: SoftwareProductPropType, + isReadOnlyMode: React.PropTypes.bool, + componentsList: React.PropTypes.arrayOf(ComponentPropType), + onDetailsSelect: React.PropTypes.func, + onAttachmentsSelect: React.PropTypes.func, + onUpload: React.PropTypes.func, + onUploadConfirmation: React.PropTypes.func, + onInvalidFileSizeUpload: React.PropTypes.func, + onComponentSelect: React.PropTypes.func, + onAddComponent: React.PropTypes.func + }; + + render() { + let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props; + return ( +
      + this.handleImportSubmit(files, isReadOnlyMode)} + onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)} + onDragLeave={() => this.setState({dragging:false})} + multiple={false} + disableClick={true} + ref='fileInput' + name='fileInput' + accept='.zip' + disabled> +
      +
      +
      + {this.renderProductSummary(currentSoftwareProduct)} + {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)} +
      +
      +
      +
      + { + componentsList.length > 0 && this.renderComponents() + } + +
      + ); + } + + handleOnDragEnter(isReadOnlyMode) { + if (!isReadOnlyMode) { + this.setState({dragging: true}); + } + } + + renderProductSummary(currentSoftwareProduct) { + let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct; + let {onDetailsSelect} = this.props; + return ( +
      +
      {i18n('Software Product Details')}
      +
      onDetailsSelect(currentSoftwareProduct)}> +
      +
      +
      +
      {name}
      +
      +
      +
      +
      +
      {i18n('Vendor')}
      +
      {vendorName}
      +
      +
      +
      {i18n('Category')}
      +
      {fullCategoryDisplayName}
      +
      +
      +
      {i18n('License Agreement')}
      +
      + {this.renderLicenseAgreement(licenseAgreementName)} +
      +
      +
      +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      +
      +
      + ); + } + + renderProductDetails(currentSoftwareProduct, isReadOnlyMode) { + let {validationData} = currentSoftwareProduct; + let {onAttachmentsSelect} = this.props; + let details = { + heatTemplates: validationData ? '1' : '0', + images: '0', + otherArtifacts: '0' + }; + + return ( +
      +
      {i18n('Software Product Attachments')}
      +
      +
      onAttachmentsSelect(currentSoftwareProduct)}> +
      +
      {i18n('HEAT Templates')} ({details.heatTemplates}) +
      +
      {i18n('Images')} ({details.images}) +
      +
      {i18n('Other Artifacts')} ({details.otherArtifacts}) +
      +
      +
      +
      +
      {i18n('Drag & drop for upload')}
      +
      {i18n('or')}
      +
      this.refs.fileInput.open()}> + {i18n('Select file')} +
      +
      +
      +
      + ); + } + + renderComponents() { + const {localFilter} = this.state; + + return ( + this.setState({localFilter: filter})}> + {this.filterList().map(component => this.renderComponentsListItem(component))} + + ); + } + + renderComponentsListItem(component) { + let {id: componentId, name, displayName, description = ''} = component; + let {currentSoftwareProduct: {id}, onComponentSelect} = this.props; + return ( + onComponentSelect({id, componentId})}> +
      +
      {i18n('Component')}
      +
      {displayName}
      +
      +
      +
      {i18n('Description')}
      +
      {description}
      +
      +
      + ); + } + + renderLicenseAgreement(licenseAgreementName) { + if (!licenseAgreementName) { + return (); + } + return (licenseAgreementName); + } + + + filterList() { + let {componentsList = []} = this.props; + + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return componentsList.filter(({displayName = '', description = ''}) => { + return escape(displayName).match(filter) || escape(description).match(filter); + }); + } + else { + return componentsList; + } + } + + handleImportSubmit(files, isReadOnlyMode) { + if (isReadOnlyMode) { + return; + } + if (files[0] && files[0].size) { + this.setState({ + fileName: files[0].name, + dragging: false, + complete: '0', + }); + this.startUploading(files); + } + else { + this.props.onInvalidFileSizeUpload(); + } + + } + + startUploading(files) { + let {onUpload, currentSoftwareProduct, onUploadConfirmation} = this.props; + + let {validationData} = currentSoftwareProduct; + + if (!(files && files.length)) { + return; + } + let file = files[0]; + let formData = new FormData(); + formData.append('upload', file); + this.refs.fileInput.value = ''; + + if (validationData) { + onUploadConfirmation(currentSoftwareProduct.id, formData); + }else { + onUpload(currentSoftwareProduct.id, formData); + } + + } +} + +export default SoftwareProductLandingPageView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js new file mode 100644 index 0000000000..dadc7777e1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductNetworksView from './SoftwareProductNetworksView.jsx'; + +export const mapStateToProps = ({softwareProduct}) => { + let {softwareProductNetworks: {networksList = []}} = softwareProduct; + return {networksList}; +}; + +export default connect(mapStateToProps, null, null, {withRef: true})(SoftwareProductNetworksView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js new file mode 100644 index 0000000000..d0e29bcfe5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductNetworksConstants.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +function baseUrl(svpId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${svpId}/networks`; +} + + +function fetchNetworksList(softwareProductId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); +} + +const SoftwareProductNetworksActionHelper = { + fetchNetworksList(dispatch, {softwareProductId, version}) { + return fetchNetworksList(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_NETWORKS, + networksList: response.results + }); + }); + } +}; + +export default SoftwareProductNetworksActionHelper; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js new file mode 100644 index 0000000000..d428d21a26 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js @@ -0,0 +1,25 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + FETCH_SOFTWARE_PRODUCT_NETWORKS: null, +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js new file mode 100644 index 0000000000..0c9c62372a --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductNetworksConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_NETWORKS: + return [...action.networksList]; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx new file mode 100644 index 0000000000..bd47467fe1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx @@ -0,0 +1,73 @@ +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'; + +class SoftwareProductNetworksView extends React.Component { + + static propTypes = { + networksList: React.PropTypes.arrayOf(React.PropTypes.shape({ + id: React.PropTypes.string.isRequired, + name: React.PropTypes.string.isRequired, + dhcp: React.PropTypes.bool.isRequired + })).isRequired + }; + + state = { + localFilter: '' + }; + + render() { + const {localFilter} = this.state; + + return ( +
      + this.setState({localFilter: filter})}> + {this.filterList().map(network => this.renderNetworksListItem(network))} + +
      + ); + } + + renderNetworksListItem(network) { + let {id, name, dhcp} = network; + return ( + + +
      +
      {i18n('Name')}
      +
      {name}
      +
      +
      +
      {i18n('DHCP')}
      +
      {dhcp ? i18n('YES') : i18n('NO')}
      +
      +
      + ); + } + + filterList() { + let {networksList} = this.props; + + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return networksList.filter(({name = ''}) => { + return escape(name).match(filter); + }); + } + else { + return networksList; + } + } +} + +export default SoftwareProductNetworksView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js new file mode 100644 index 0000000000..5c3a8dae01 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; + +import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; +import SoftwareProductProcessesView from './SoftwareProductProcessesView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; + let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct); + let {data} = processesEditor; + + return { + currentSoftwareProduct, + processesList, + isDisplayEditor: Boolean(data), + isModalInEditMode: Boolean(data && data.id), + isReadOnlyMode + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + return { + onAddProcess: () => SoftwareProductProcessesActionHelper.openEditor(dispatch), + onEditProcess: (process) => SoftwareProductProcessesActionHelper.openEditor(dispatch, process), + onDeleteProcess: (process) => SoftwareProductProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId}) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductProcessesView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js new file mode 100644 index 0000000000..df5d08ffe5 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js @@ -0,0 +1,151 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductProcessesConstants.js'; +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; + +function baseUrl(svpId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/vendor-software-products/${svpId}/processes`; +} + +function putProcess(softwareProductId, process) { + return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${process.id}`, { + name: process.name, + description: process.description + }); +} + +function postProcess(softwareProductId, process) { + return RestAPIUtil.create(`${baseUrl(softwareProductId)}`, { + name: process.name, + description: process.description + }); +} + +function deleteProcess(softwareProductId, processId) { + return RestAPIUtil.destroy(`${baseUrl(softwareProductId)}/${processId}`); +} + +function uploadFileToProcess(softwareProductId, processId, formData) +{ + return RestAPIUtil.create(`${baseUrl(softwareProductId)}/${processId}/upload`, formData); +} + +function fetchProcesses(softwareProductId, version) { + let versionQuery = version ? `?version=${version}` : ''; + return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`); +} + + + +const SoftwareProductActionHelper = { + + fetchProcessesList(dispatch, {softwareProductId, version}) { + + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES, + processesList: [] + }); + + return fetchProcesses(softwareProductId, version).then(response => { + dispatch({ + type: actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES, + processesList: response.results + }); + }); + }, + openEditor(dispatch, process = {}) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN, + process + }); + }, + + deleteProcess(dispatch, {process, softwareProductId}) { + return deleteProcess(softwareProductId, process.id).then(() => { + dispatch({ + type: actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS, + processId: process.id + }); + }); + + }, + + closeEditor(dispatch) { + dispatch({ + type:actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE + }); + }, + + processEditorDataChanged(dispatch, {deltaData}) { + dispatch({ + type: actionTypes.processEditor.DATA_CHANGED, + deltaData + }); + }, + + saveProcess(dispatch, {softwareProductId, previousProcess, process}) { + if (previousProcess) { + return putProcess(softwareProductId, process).then(() => { + if (process.formData){ + uploadFileToProcess(softwareProductId, process.id, process.formData); + } + dispatch({ + type: actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS, + process + }); + }); + } + else { + return postProcess(softwareProductId, process).then(response => { + if (process.formData) { + uploadFileToProcess(softwareProductId, response.value, process.formData); + } + dispatch({ + type: actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS, + process: { + ...process, + id: response.value + } + }); + }); + } + }, + + hideDeleteConfirm(dispatch) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM, + processToDelete: false + }); + }, + + openDeleteProcessesConfirm(dispatch, {process} ) { + dispatch({ + type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM, + processToDelete: process + }); + } + +}; + +export default SoftwareProductActionHelper; + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx new file mode 100644 index 0000000000..0159352dae --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx'; +import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js'; + +function renderMsg(processToDelete) { + let name = processToDelete ? processToDelete.name : ''; + let msg = i18n('Are you sure you want to delete "{name}"?', {name}); + return ( +
      +

      {msg}

      +
      + ); +}; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductEditor, softwareProductProcesses} = softwareProduct; + let {processToDelete} = softwareProductProcesses; + let softwareProductId = softwareProductEditor.data.id; + + const show = processToDelete !== false; + return { + show, + title: i18n('Warning!'), + type: 'warning', + msg: renderMsg(processToDelete), + confirmationDetails: {processToDelete, softwareProductId} + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + onConfirmed: ({processToDelete, softwareProductId}) => { + SoftwareProductProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId}); + SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch); + }, + onDeclined: () => { + SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch); + } + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js new file mode 100644 index 0000000000..63f3067a89 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + ADD_SOFTWARE_PRODUCT_PROCESS: null, + EDIT_SOFTWARE_PRODUCT_PROCESS: null, + DELETE_SOFTWARE_PRODUCT_PROCESS: null, + SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: null, + SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: null, + FETCH_SOFTWARE_PRODUCT_PROCESSES: null, + SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM: null, + processEditor: { + DATA_CHANGED: null + } +}); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js new file mode 100644 index 0000000000..8dc48c50b1 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {connect} from 'react-redux'; +import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper'; +import SoftwareProductProcessesEditorView from './SoftwareProductProcessesEditorView.jsx'; + +const mapStateToProps = ({softwareProduct}) => { + let {softwareProductProcesses: {processesList, processesEditor}} = softwareProduct; + let {data} = processesEditor; + + let previousData; + const processId = data ? data.id : null; + if(processId) { + previousData = processesList.find(process => process.id === processId); + } + + return { + data, + previousData + }; +}; + +const mapActionsToProps = (dispatch, {softwareProductId}) => { + return { + onDataChanged: deltaData => SoftwareProductProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}), + onSubmit: ({previousProcess, process}) => { + SoftwareProductProcessesActionHelper.closeEditor(dispatch); + SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, process}); + }, + onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductProcessesEditorView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js new file mode 100644 index 0000000000..cae25e2c89 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductProcessesConstants.js'; + +export default (state = {}, action) => { + switch (action.type) { + case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: + return { + ...state, + data: action.process + }; + case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: + return {}; + + case actionTypes.processEditor.DATA_CHANGED: + return { + ...state, + data: { + ...state.data, + ...action.deltaData + } + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx new file mode 100644 index 0000000000..c2c4aff382 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import Dropzone from 'react-dropzone'; +import classnames from 'classnames'; + +import i18n from 'nfvo-utils/i18n/i18n.js'; +import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx'; +import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; + +const SoftwareProductProcessEditorPropType = React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string, + description: React.PropTypes.string, + artifactName: React.PropTypes.string +}); + +class SoftwareProductProcessesEditorView extends React.Component { + + state = { + dragging: false, + files: [] + }; + + static propTypes = { + data: SoftwareProductProcessEditorPropType, + previousData: SoftwareProductProcessEditorPropType, + isReadOnlyMode: React.PropTypes.bool, + onDataChanged: React.PropTypes.func, + onSubmit: React.PropTypes.func, + onClose: React.PropTypes.func + }; + + render() { + let {data = {}, isReadOnlyMode, onDataChanged, onClose} = this.props; + let {name, description, artifactName} = data; + return ( + this.submit() } + onReset={ () => onClose() } + className='vsp-processes-editor'> +
      + this.handleImportSubmit(files)} + onDragEnter={() => this.setState({dragging: true})} + onDragLeave={() => this.setState({dragging: false})} + multiple={false} + disableClick={true} + ref='processEditorFileInput' + name='processEditorFileInput' + accept='*.*'> +
      +
      + onDataChanged({name})} + label={i18n('Name')} + value={name} + validations={{validateName: true, maxLength: 120, required: true}} + type='text'/> + +
      +
      +
      +
      {i18n('Drag & drop for upload')}
      +
      {i18n('or')}
      +
      this.refs.processEditorFileInput.open()}> + {i18n('Select file')} +
      +
      +
      +
      + onDataChanged({description})} + label={i18n('Notes')} + value={description} + name='vsp-process-description' + className='vsp-process-description' + validations={{maxLength: 1000}} + type='textarea'/> +
      +
      +
      + ); + } + + submit() { + const {data: process, previousData: previousProcess} = this.props; + let {files} = this.state; + let formData = false; + if (files.length) { + let file = files[0]; + formData = new FormData(); + formData.append('upload', file); + } + + let updatedProcess = { + ...process, + formData + }; + this.props.onSubmit({process: updatedProcess, previousProcess}); + } + + + handleImportSubmit(files) { + let {onDataChanged} = this.props; + this.setState({ + dragging: false, + complete: '0', + files + }); + onDataChanged({artifactName: files[0].name}); + } +} + +export default SoftwareProductProcessesEditorView; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js new file mode 100644 index 0000000000..619a2dba0f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {actionTypes} from './SoftwareProductProcessesConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES: + return [...action.processesList]; + case actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS: + const indexForEdit = state.findIndex(process => process.id === action.process.id); + return [...state.slice(0, indexForEdit), action.process, ...state.slice(indexForEdit + 1)]; + case actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS: + return [...state, action.process]; + case actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS: + return state.filter(process => process.id !== action.processId); + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx new file mode 100644 index 0000000000..a2aa3d414e --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx @@ -0,0 +1,112 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import Modal from 'nfvo-components/modal/Modal.jsx'; + +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx'; + +import SoftwareProductProcessesEditor from './SoftwareProductProcessesEditor.js'; +import SoftwareProductProcessesConfirmationModal from './SoftwareProductProcessesConfirmationModal.jsx'; + + +class SoftwareProductProcessesView extends React.Component { + + state = { + localFilter: '' + }; + + static propTypes = { + onAddProcess: React.PropTypes.func.isRequired, + onEditProcess: React.PropTypes.func.isRequired, + onDeleteProcess: React.PropTypes.func.isRequired, + isDisplayEditor: React.PropTypes.bool.isRequired, + isReadOnlyMode: React.PropTypes.bool.isRequired + }; + + render() { + let { currentSoftwareProduct} = this.props; + return ( +
      + {this.renderEditor()} + {this.renderProcessList()} + +
      + ); + } + + renderEditor() { + let {currentSoftwareProduct: {id}, isModalInEditMode, isReadOnlyMode, isDisplayEditor} = this.props; + return ( + + + + {isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')} + + + + + + ); + } + + renderProcessList() { + const {localFilter} = this.state; + let {onAddProcess, isReadOnlyMode} = this.props; + + return ( + this.setState({localFilter: filter})}> + {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))} + + ); + } + + renderProcessListItem(process, isReadOnlyMode) { + let {id, name, description, artifactName = ''} = process; + let {onEditProcess, onDeleteProcess} = this.props; + return ( + onEditProcess(process)} + onDelete={() => onDeleteProcess(process)}> + +
      +
      {i18n('Name')}
      +
      {name}
      +
      +
      +
      {i18n('Artifact name')}
      +
      {artifactName}
      +
      +
      +
      {i18n('Notes')}
      +
      {description}
      +
      +
      + ); + } + + filterList() { + let {processesList} = this.props; + let {localFilter} = this.state; + + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return processesList.filter(({name = '', description = ''}) => { + return escape(name).match(filter) || escape(description).match(filter); + }); + } + else { + return processesList; + } + } +} + +export default SoftwareProductProcessesView; diff --git a/openecomp-ui/src/sdc-app/punch-outs.js b/openecomp-ui/src/sdc-app/punch-outs.js new file mode 100644 index 0000000000..46e84a60a4 --- /dev/null +++ b/openecomp-ui/src/sdc-app/punch-outs.js @@ -0,0 +1,29 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import '../../resources/scss/onboarding.scss'; +import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; + +import 'core-js/fn/array/includes.js'; +import OnboardingPunchOut from './onboarding/OnboardingPunchOut.jsx'; +import FlowsPunchOut from './flows/FlowsPunchOut.jsx'; + +PunchOutRegistry.register('onboarding/vendor', () => new OnboardingPunchOut()); +PunchOutRegistry.register('sequence-diagram', () => new FlowsPunchOut()); diff --git a/openecomp-ui/src/sdc-app/sdc.app.jsx b/openecomp-ui/src/sdc-app/sdc.app.jsx new file mode 100644 index 0000000000..0929fa0bb9 --- /dev/null +++ b/openecomp-ui/src/sdc-app/sdc.app.jsx @@ -0,0 +1,18 @@ +import '../../resources/scss/bootstrap.scss'; +import '../../resources/css/font-awesome.min.css'; +import 'react-select/dist/react-select.min.css'; +import 'dox-sequence-diagram-ui/src/main/webapp/res/sdc-sequencer.scss'; +import '../../resources/scss/style.scss'; + +import React from 'react'; +import ReactDOM from 'react-dom'; + +import Application from './Application.jsx'; +import Modules from './ModulesOptions.jsx'; + +//chrome 48 remove svg method which is used in jointjs core -> https://github.com/cpettitt/dagre-d3/issues/202 --> http://jointjs.com/blog/get-transform-to-element-polyfill.html +SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(toElement) { + return toElement.getScreenCTM().inverse().multiply(this.getScreenCTM()); +}; + +ReactDOM.render(, document.getElementById('sdc-app')); diff --git a/openecomp-ui/test-utils/MockRest.js b/openecomp-ui/test-utils/MockRest.js new file mode 100644 index 0000000000..927da6030a --- /dev/null +++ b/openecomp-ui/test-utils/MockRest.js @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +const queue = { + fetch: [], + save: [], + create: [], + destroy: [] +}; + +const initQueue = () => { + queue['fetch'] = []; + queue['save'] = []; + queue['create'] = []; + queue['destroy'] = []; +}; + +const handleOperation = (handler, options) => { + if(typeof handler === 'function') { + return Promise.resolve(handler(options)); + } + else { + return Promise.resolve(handler); + } +}; + +export default { + + fetch(baseUrl, options) { + const {fetch} = queue; + if(!fetch.length) { + throw new Error(`Fetch operation was called without proper handler. baseUrl: '${baseUrl}' options: '${options}'`); + } + return handleOperation(fetch.shift(), {options, baseUrl}); + }, + + save(baseUrl, data, options) { + const {save} = queue; + if(!save.length) { + throw new Error(`Save operation was called without proper handler. baseUrl: '${baseUrl}' options: '${options}'`); + } + return handleOperation(save.shift(), {data, options, baseUrl}); + }, + + create(baseUrl, data, options) { + const {create} = queue; + if(!create.length) { + throw new Error(`Create operation was called without proper handler. baseUrl: '${baseUrl}' options: '${options}'`); + } + return handleOperation(create.shift(), {data, options, baseUrl}); + }, + + destroy(baseUrl, options) { + const {destroy} = queue; + if(!destroy.length) { + throw new Error(`Destroy operation was called without proper handler. baseUrl: '${baseUrl}' options: '${options}'`); + } + return handleOperation(destroy.shift(), {options, baseUrl}); + }, + + addHandler(operation, handler) { + queue[operation].push(handler); + }, + + resetQueue() { + initQueue(); + } +}; diff --git a/openecomp-ui/test-utils/Util.js b/openecomp-ui/test-utils/Util.js new file mode 100644 index 0000000000..7146267afe --- /dev/null +++ b/openecomp-ui/test-utils/Util.js @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import deepFreeze from 'deep-freeze'; +import ReactTestUtils from 'react-addons-test-utils'; + +//returned object should be treated as immutable. +export const cloneAndSet = (obj, path, value) => { + let retVal = {...obj}; + let inner = retVal; + + if (typeof path === 'string') { + path = path.split('.'); + } + + for (let i = 0; i < path.length - 1; i++) { + inner[path[i]] = { + ...inner[path[i]] + }; + inner = inner[path[i]]; + } + inner[path[path.length - 1]] = value; + return deepFreeze(retVal); +}; + +/** + * array findAllRenderedDOMComponentsWithTestId( + ReactComponent tree, + function test + ) + * @param tree - ReactComponent + * @param testId - string + * @returns {Array.} + */ +export const findAllRenderedComponentsWithTestId = (tree, testId) => { + return ReactTestUtils.findAllInRenderedTree(tree, component => component.props.testId === testId); +}; + diff --git a/openecomp-ui/test/flows/FlowsListEditor.test.js b/openecomp-ui/test/flows/FlowsListEditor.test.js new file mode 100644 index 0000000000..534253567e --- /dev/null +++ b/openecomp-ui/test/flows/FlowsListEditor.test.js @@ -0,0 +1,279 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/flows/FlowsListEditor.js'; +import FlowsListEditorView from 'sdc-app/flows/FlowsListEditorView.jsx'; + +describe('Flows List Editor Mapper and View Classes: ', function () { + + it('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + it('mapStateToProps mapper - without flowList', () => { + let flows = { + isDisplayModal: true, + isModalInEditMode: false, + shouldShowWorkflowsEditor: undefined + }; + let results = mapStateToProps({flows}); + expect(results.flowList).toExist(); + expect(results.flowList.length).toEqual(0); + expect(results.shouldShowWorkflowsEditor).toBe(true); + }); + + it('mapStateToProps mapper - populated flowList', () => { + let artifactName = 'test1', description = 'desc'; + let flows = { + flowList: [{artifactName, description}], + isDisplayModal: true, + isModalInEditMode: false, + shouldShowWorkflowsEditor: false + }; + let results = mapStateToProps({flows}); + expect(results.flowList).toExist(); + expect(results.flowList.length).toEqual(1); + expect(results.shouldShowWorkflowsEditor).toBe(false); + }); + + it('mapStateToProps mapper - populated flowList and currentFlow is in readonly', () => { + let artifactName = 'test1', description = 'desc'; + let currentFlow = {artifactName, description, readonly: true}; + let flows = { + flowList: [currentFlow], + currentFlow, + isDisplayModal: true, + isModalInEditMode: false, + shouldShowWorkflowsEditor: false + }; + let results = mapStateToProps({flows}); + expect(results.currentFlow).toExist(); + expect(results.isCheckedOut).toEqual(false); + }); + + it('mapStateToProps mapper - populated flowList and currentFlow is in not readonly', () => { + let artifactName = 'test1', description = 'desc'; + let currentFlow = {artifactName, description, readonly: false}; + let flows = { + flowList: [currentFlow], + currentFlow, + isDisplayModal: true, + isModalInEditMode: false, + shouldShowWorkflowsEditor: false + }; + let results = mapStateToProps({flows}); + expect(results.currentFlow).toExist(); + expect(results.isCheckedOut).toEqual(true); + }); + + it('basic view component run with empty flowList and should show the list', () => { + let renderer = TestUtils.createRenderer(); + let artifactName = 'test1', description = 'desc'; + let currentFlow = {artifactName, description, readonly: false}; + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('basic view component run with empty flowList and should show the diagram', () => { + const flow = { + 'artifactType': 'WORKFLOW', + 'participants': [ + { + 'id': '1', + 'name': 'Customer' + }, + { + 'id': '2', + 'name': 'CCD' + }, + { + 'id': '3', + 'name': 'Infrastructure' + }, + { + 'id': '4', + 'name': 'MSO' + }, + { + 'id': '5', + 'name': 'SDN-C' + }, + { + 'id': '6', + 'name': 'A&AI' + }, + { + 'id': '7', + 'name': 'APP-C' + }, + { + 'id': '8', + 'name': 'Cloud' + }, + { + 'id': '9', + 'name': 'DCAE' + }, + { + 'id': '10', + 'name': 'ALTS' + }, + { + 'id': '11', + 'name': 'VF' + } + ], + 'serviceID': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b', + 'artifactDisplayName': 'zizizi', + 'artifactGroupType': 'INFORMATIONAL', + 'uniqueId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + 'artifactName': 'zizizi', + 'artifactLabel': 'zizizi', + 'artifactUUID': '0295a7cc-8c02-4105-9d7e-c30ce67ecd07', + 'artifactVersion': '1', + 'creationDate': 1470144601623, + 'lastUpdateDate': 1470144601623, + 'description': 'aslkjdfl asfdasdf', + 'mandatory': false, + 'timeout': 0, + 'esId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + 'artifactChecksum': 'NjBmYjc4NGM5MWIwNmNkMDhmMThhMDAwYmQxYjBiZTU=', + 'heatParameters': [], + 'sequenceDiagramModel': { + 'diagram': { + 'metadata': { + 'id': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + 'name': 'zizizi', + 'ref': 'BLANK' + }, + 'lifelines': [ + { + 'id': '1', + 'name': 'Customer', + 'index': 1, + 'x': 175 + }, + { + 'id': '2', + 'name': 'CCD', + 'index': 2, + 'x': 575 + }, + { + 'id': '3', + 'name': 'Infrastructure', + 'index': 3, + 'x': 975 + }, + { + 'id': '4', + 'name': 'MSO', + 'index': 4, + 'x': 1375 + }, + { + 'id': '5', + 'name': 'SDN-C', + 'index': 5, + 'x': 1775 + }, + { + 'id': '6', + 'name': 'A&AI', + 'index': 6, + 'x': 2175 + }, + { + 'id': '7', + 'name': 'APP-C', + 'index': 7, + 'x': 2575 + }, + { + 'id': '8', + 'name': 'Cloud', + 'index': 8, + 'x': 2975 + }, + { + 'id': '9', + 'name': 'DCAE', + 'index': 9, + 'x': 3375 + }, + { + 'id': '10', + 'name': 'ALTS', + 'index': 10, + 'x': 3775 + }, + { + 'id': '11', + 'name': 'VF', + 'index': 11, + 'x': 4175 + } + ], + 'steps': [ + { + 'message': { + 'id': '9377-5036-c011-cb95-3a8b-82c6-bbb5-bc84', + 'name': '[Unnamed Message]', + 'type': 'request', + 'from': '1', + 'to': '2', + 'index': 1 + } + }, + { + 'message': { + 'id': '64c4-4fd1-b1da-4355-a060-6e48-ee47-c85c', + 'name': '[Unnamed Message]', + 'type': 'request', + 'from': '1', + 'to': '2', + 'index': 2 + } + } + ] + } + } + }; + let renderer = TestUtils.createRenderer(); + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('basic view component run with empty flowList and should show popup modal', () => { + let renderer = TestUtils.createRenderer(); + let artifactName = 'test1', description = 'desc'; + let currentFlow = {artifactName, description, readonly: false}; + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + +}); diff --git a/openecomp-ui/test/flows/flowsEditorModal.test.js b/openecomp-ui/test/flows/flowsEditorModal.test.js new file mode 100644 index 0000000000..d8da97af4e --- /dev/null +++ b/openecomp-ui/test/flows/flowsEditorModal.test.js @@ -0,0 +1,89 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/flows/FlowsEditorModal.js'; +import FlowsEditorModalView from 'sdc-app/flows/FlowsEditorModalView.jsx'; + +describe('Flows Editor Modal Mapper and View Classes: ', function () { + + it('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + it('mapStateToProps mapper - without currentFlow', () => { + var flows = { + serviceID: '123', + diagramType: 'SOME_TYPE' + }; + var results = mapStateToProps({flows}); + expect(results.currentFlow).toExist(); + expect(results.currentFlow.artifactName).toBe(''); + expect(results.currentFlow.description).toBe(''); + }); + + it('mapStateToProps mapper - populated currentFlow', () => { + let artifactName = 'test1', description = 'desc'; + var flows = { + currentFlow: {artifactName, description}, + serviceID: '123', + diagramType: 'SOME_TYPE' + }; + var results = mapStateToProps({flows}); + expect(results.currentFlow).toExist(); + expect(results.currentFlow.artifactName).toBe(artifactName); + expect(results.currentFlow.description).toBe(description); + expect(results.currentFlow.serviceID).toBe(flows.serviceID); + expect(results.currentFlow.artifactType).toBe(flows.diagramType); + }); + + it('basic modal view component run with empty artifact', () => { + let renderer = TestUtils.createRenderer(); + renderer.render( + {}} + onDataChanged={()=>{}} + currentFlow={{artifactName: '', description: ''}}/>); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('modal view component run with data changed handler', done => { + let handler = () => done(); + let document = TestUtils.renderIntoDocument( + {}} + onDataChanged={handler} + currentFlow={{artifactName: '', description: ''}}/>); + let result = TestUtils.scryRenderedDOMComponentsWithTag(document, 'input'); + expect(result).toExist(); + expect(result.length).toExist(); + TestUtils.Simulate.change(result[0]); + }); + + it('modal view component - on save click', done => { + let handler = () => done(); + var flowsEditorModalView = new FlowsEditorModalView({currentFlow: {}, onSubmit: handler}); + flowsEditorModalView.onSaveClicked(); + }); + +}); diff --git a/openecomp-ui/test/flows/test.js b/openecomp-ui/test/flows/test.js new file mode 100644 index 0000000000..4c5ab78640 --- /dev/null +++ b/openecomp-ui/test/flows/test.js @@ -0,0 +1,497 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import store from 'sdc-app/AppStore.js'; +import FlowsActions from 'sdc-app/flows/FlowsActions.js'; +import {enums} from 'sdc-app/flows/FlowsConstants.js'; + +const NEW_FLOW = true; + +let assertFlowDataAfterCreateFetchAndUpdate = (data) => { + let {flowList, serviceID, diagramType} = store.getState().flows; + expect(serviceID).toBe(data.serviceID); + expect(diagramType).toBe(data.artifactType); + let uniqueId = data.uniqueId || `${data.serviceID}.${data.artifactName}`; + let index = flowList.findIndex(flow => flow.uniqueId === uniqueId); + expect(index).toNotBe(-1); +}; + +describe('Workflows and Management Flows Module Tests:', function () { + + + it('empty artifact should open flow creation modal', done => { + + const artifacts = {}; + + deepFreeze(store.getState()); + deepFreeze(artifacts); + FlowsActions.fetchFlowArtifacts(store.dispatch, { + artifacts, + diagramType: enums.WORKFLOW, + participants: [], + serviceID: '1234' + }); + setTimeout(() => { + let state = store.getState(); + expect(state.flows.isDisplayModal).toBe(true); + expect(state.flows.isModalInEditMode).toBe(false); + done(); + }, 50); + }); + + it('Close flow details editor modal', done => { + deepFreeze(store.getState()); + FlowsActions.closeFlowDetailsEditor(store.dispatch); + setTimeout(() => { + let state = store.getState(); + expect(state.flows.isDisplayModal).toBe(false); + expect(state.flows.isModalInEditMode).toBe(false); + done(); + }, 50); + }); + + it('Get Flows List from loaded artifact', done => { + + deepFreeze(store.getState()); + + const artifacts = { + 'test1': { + 'uniqueId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.test1', + 'artifactType': 'NETWORK_CALL_FLOW', + 'artifactName': 'test1', + 'artifactChecksum': 'MzYxZGIyNjlkNjRmMTM4ZWMxM2FjNDUyNDQwMTI3NzM=', + 'attUidLastUpdater': 'cs0008', + 'updaterFullName': 'Carlos Santana', + 'creationDate': 1468164899724, + 'lastUpdateDate': 1468164899724, + 'esId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.test1', + 'artifactLabel': 'test1', + 'artifactCreator': 'cs0008', + 'description': 'www', + 'mandatory': false, + 'artifactDisplayName': 'test1', + 'serviceApi': false, + 'artifactGroupType': 'INFORMATIONAL', + 'timeout': 0, + 'artifactVersion': '1', + 'artifactUUID': '28d4cb95-bb46-4666-b858-e333671e6444', + 'payloadUpdateDate': 1468164900232 + }, + 'kukuriku': { + 'uniqueId': '0280b577-2c7b-426e-b7a2-f0dc16508c37.kukuriku', + 'artifactType': 'PUPPET', + 'artifactName': 'fuel.JPG', + 'artifactChecksum': 'OWEyYTVjMWFiNWQ4ZDIwZDUxYTE3Y2EzZmI3YTYyMjA=', + 'attUidLastUpdater': 'cs0008', + 'updaterFullName': 'Carlos Santana', + 'creationDate': 1467877631512, + 'lastUpdateDate': 1467877631512, + 'esId': '0280b577-2c7b-426e-b7a2-f0dc16508c37.kukuriku', + 'artifactLabel': 'kukuriku', + 'artifactCreator': 'cs0008', + 'description': 'asdfasdf', + 'mandatory': false, + 'artifactDisplayName': 'kukuriku', + 'serviceApi': false, + 'artifactGroupType': 'INFORMATIONAL', + 'timeout': 0, + 'artifactVersion': '1', + 'artifactUUID': 'c1e98336-03f4-4b2a-b6a5-08eca44fe3c4', + 'payloadUpdateDate': 1467877632722 + }, + 'test3': { + 'uniqueId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.test3', + 'artifactType': 'NETWORK_CALL_FLOW', + 'artifactName': 'test3', + 'artifactChecksum': 'ZmJkZGU1M2M2ZWUxZTdmNGU5NTNiNTdiYTAzMmM1YzU=', + 'attUidLastUpdater': 'cs0008', + 'updaterFullName': 'Carlos Santana', + 'creationDate': 1468165068570, + 'lastUpdateDate': 1468165128827, + 'esId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.test3', + 'artifactLabel': 'test3', + 'artifactCreator': 'cs0008', + 'description': '333', + 'mandatory': false, + 'artifactDisplayName': 'test3', + 'serviceApi': false, + 'artifactGroupType': 'INFORMATIONAL', + 'timeout': 0, + 'artifactVersion': '2', + 'artifactUUID': '0988027c-d19c-43db-8315-2c68fc773775', + 'payloadUpdateDate': 1468165129335 + } + }; + + const artifactsArray = Object.keys(artifacts).map(artifact => artifact); + + deepFreeze(artifacts); + + deepFreeze(store.getState()); + + let actionData = { + artifacts, + diagramType: enums.WORKFLOW, + participants: [], + serviceID: '1234' + }; + FlowsActions.fetchFlowArtifacts(store.dispatch, actionData); + + setTimeout(() => { + let state = store.getState(); + expect(state.flows.isDisplayModal).toBe(false); + expect(state.flows.isModalInEditMode).toBe(false); + expect(state.flows.flowList.length).toEqual(artifactsArray.length); + expect(state.flows.flowParticipants).toEqual(actionData.participants); + expect(state.flows.serviceID).toBe(actionData.serviceID); + expect(state.flows.diagramType).toBe(actionData.diagramType); + done(); + }, 50); + + }); + + + it('Add New Flow', done => { + + deepFreeze(store.getState()); + + const flowCreateData = deepFreeze({ + artifactName: 'zizizi', + artifactType: 'WORKFLOW', + description: 'aslkjdfl asfdasdf', + serviceID: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b', + }); + + + let expectedDataToBeSentInTheRequest = { + artifactGroupType: 'INFORMATIONAL', + artifactLabel: 'zizizi', + artifactName: 'zizizi', + artifactType: 'WORKFLOW', + description: 'aslkjdfl asfdasdf', + payloadData: 'eyJWRVJTSU9OIjp7Im1ham9yIjoxLCJtaW5vciI6MH0sImRlc2NyaXB0aW9uIjoiYXNsa2pkZmwgYXNmZGFzZGYifQ==' + }; + mockRest.addHandler('create', ({data, baseUrl, options}) => { + expect(baseUrl).toBe(`/sdc1/feProxy/rest/v1/catalog/services/${flowCreateData.serviceID}/artifacts/`); + expect(data.artifactLabel).toBe(expectedDataToBeSentInTheRequest.artifactLabel); + expect(data.artifactName).toBe(expectedDataToBeSentInTheRequest.artifactName); + expect(data.artifactType).toBe(expectedDataToBeSentInTheRequest.artifactType); + expect(data.description).toBe(expectedDataToBeSentInTheRequest.description); + expect(data.payloadData).toBe(expectedDataToBeSentInTheRequest.payloadData); + expect(options.md5).toBe(true); + return { + artifactChecksum: 'NjBmYjc4NGM5MWIwNmNkMDhmMThhMDAwYmQxYjBiZTU=', + artifactCreator: 'cs0008', + artifactDisplayName: 'zizizi', + artifactGroupType: 'INFORMATIONAL', + artifactLabel: 'zizizi', + artifactName: 'zizizi', + artifactType: 'WORKFLOW', + artifactUUID: '0295a7cc-8c02-4105-9d7e-c30ce67ecd07', + artifactVersion: '1', + attUidLastUpdater: 'cs0008', + creationDate: 1470144601623, + description: 'aslkjdfl asfdasdf', + esId: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + lastUpdateDate: 1470144601623, + mandatory: false, + payloadUpdateDate: 1470144602131, + serviceApi: false, + timeout: 0, + uniqueId: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + updaterFullName: 'Carlos Santana', + }; + }); + + FlowsActions.createOrUpdateFlow(store.dispatch, {flow: flowCreateData}, NEW_FLOW); + + setTimeout(() => { + assertFlowDataAfterCreateFetchAndUpdate(flowCreateData); + done(); + }, 50); + }); + + it('Fetch Flow', done => { + + deepFreeze(store.getState()); + + const flowFetchData = { + artifactName: 'zizizi', + artifactType: 'WORKFLOW', + description: 'aslkjdfl asfdasdf', + serviceID: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b', + uniqueId: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + participants: [] + }; + + mockRest.addHandler('fetch', ({baseUrl}) => { + //sdc1/feProxy/rest/v1/catalog/services/338d75f0-aec8-4eb4-89c9-8733fcd9bf3b/artifacts/338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi + expect(baseUrl).toBe(`/sdc1/feProxy/rest/v1/catalog/services/${flowFetchData.serviceID}/artifacts/${flowFetchData.uniqueId}`); + return { + artifactName: 'zizizi', + base64Contents: 'eyJWRVJTSU9OIjp7Im1ham9yIjoxLCJtaW5vciI6MH0sImRlc2NyaXB0aW9uIjoiYXNsa2pkZmwgYXNmZGFzZGYifQ==' + }; + }); + + FlowsActions.fetchArtifact(store.dispatch, {flow: flowFetchData}); + + setTimeout(() => { + assertFlowDataAfterCreateFetchAndUpdate(flowFetchData); + done(); + }, 50); + }); + + it('Update Existing Flow', done => { + + deepFreeze(store.getState()); + + const flowUpdateData = { + 'artifactType': 'WORKFLOW', + 'participants': [ + { + 'id': '1', + 'name': 'Customer' + }, + { + 'id': '2', + 'name': 'CCD' + }, + { + 'id': '3', + 'name': 'Infrastructure' + }, + { + 'id': '4', + 'name': 'MSO' + }, + { + 'id': '5', + 'name': 'SDN-C' + }, + { + 'id': '6', + 'name': 'A&AI' + }, + { + 'id': '7', + 'name': 'APP-C' + }, + { + 'id': '8', + 'name': 'Cloud' + }, + { + 'id': '9', + 'name': 'DCAE' + }, + { + 'id': '10', + 'name': 'ALTS' + }, + { + 'id': '11', + 'name': 'VF' + } + ], + 'serviceID': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b', + 'artifactDisplayName': 'zizizi', + 'artifactGroupType': 'INFORMATIONAL', + 'uniqueId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + 'artifactName': 'zizizi', + 'artifactLabel': 'zizizi', + 'artifactUUID': '0295a7cc-8c02-4105-9d7e-c30ce67ecd07', + 'artifactVersion': '1', + 'creationDate': 1470144601623, + 'lastUpdateDate': 1470144601623, + 'description': 'aslkjdfl asfdasdf', + 'mandatory': false, + 'timeout': 0, + 'esId': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + 'artifactChecksum': 'NjBmYjc4NGM5MWIwNmNkMDhmMThhMDAwYmQxYjBiZTU=', + 'heatParameters': [], + 'sequenceDiagramModel': { + 'diagram': { + 'metadata': { + 'id': '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + 'name': 'zizizi', + 'ref': 'BLANK' + }, + 'lifelines': [ + { + 'id': '1', + 'name': 'Customer', + 'index': 1, + 'x': 175 + }, + { + 'id': '2', + 'name': 'CCD', + 'index': 2, + 'x': 575 + }, + { + 'id': '3', + 'name': 'Infrastructure', + 'index': 3, + 'x': 975 + }, + { + 'id': '4', + 'name': 'MSO', + 'index': 4, + 'x': 1375 + }, + { + 'id': '5', + 'name': 'SDN-C', + 'index': 5, + 'x': 1775 + }, + { + 'id': '6', + 'name': 'A&AI', + 'index': 6, + 'x': 2175 + }, + { + 'id': '7', + 'name': 'APP-C', + 'index': 7, + 'x': 2575 + }, + { + 'id': '8', + 'name': 'Cloud', + 'index': 8, + 'x': 2975 + }, + { + 'id': '9', + 'name': 'DCAE', + 'index': 9, + 'x': 3375 + }, + { + 'id': '10', + 'name': 'ALTS', + 'index': 10, + 'x': 3775 + }, + { + 'id': '11', + 'name': 'VF', + 'index': 11, + 'x': 4175 + } + ], + 'steps': [ + { + 'message': { + 'id': '9377-5036-c011-cb95-3a8b-82c6-bbb5-bc84', + 'name': '[Unnamed Message]', + 'type': 'request', + 'from': '1', + 'to': '2', + 'index': 1 + } + }, + { + 'message': { + 'id': '64c4-4fd1-b1da-4355-a060-6e48-ee47-c85c', + 'name': '[Unnamed Message]', + 'type': 'request', + 'from': '1', + 'to': '2', + 'index': 2 + } + } + ] + } + } + }; + + mockRest.addHandler('create', ({baseUrl}) => { + expect(baseUrl).toBe(`/sdc1/feProxy/rest/v1/catalog/services/${flowUpdateData.serviceID}/artifacts/${flowUpdateData.uniqueId}`); + + return { + artifactChecksum: 'MmE5MWJmN2ZlN2FhM2JhMzA0NGQ1ODMyOWFhNWI0NDA=', + artifactCreator: 'cs0008', + artifactDisplayName: 'zizizi', + artifactGroupType: 'INFORMATIONAL', + artifactLabel: 'zizizi', + artifactName: 'zizizi', + artifactType: 'WORKFLOW', + artifactUUID: '3319335b-969e-4d72-b5a2-409645de6d64', + artifactVersion: '3', + attUidLastUpdater: 'cs0008', + creationDate: 1470144601623, + description: 'aslkjdfl asfdasdf', + esId: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + lastUpdateDate: 1470208425904, + mandatory: false, + payloadUpdateDate: 1470208426424, + serviceApi: false, + timeout: 0, + uniqueId: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + updaterFullName: 'Carlos Santana' + }; + }); + + FlowsActions.createOrUpdateFlow(store.dispatch, {flow: flowUpdateData}, !NEW_FLOW); + + setTimeout(() => { + assertFlowDataAfterCreateFetchAndUpdate(flowUpdateData); + done(); + }, 50); + }); + + it('Delete Flow', done => { + + deepFreeze(store.getState()); + + const flowDeleteData = deepFreeze({ + artifactName: 'zizizi', + artifactType: 'WORKFLOW', + description: 'aslkjdfl asfdasdf', + serviceID: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b', + uniqueId: '338d75f0-aec8-4eb4-89c9-8733fcd9bf3b.zizizi', + participants: [] + }); + + mockRest.addHandler('destroy', ({baseUrl}) => { + expect(baseUrl).toBe(`/sdc1/feProxy/rest/v1/catalog/services/${flowDeleteData.serviceID}/artifacts/${flowDeleteData.uniqueId}`); + return {}; + }); + + FlowsActions.deleteFlow(store.dispatch, {flow: flowDeleteData}); + + setTimeout(() => { + let {flowList} = store.getState().flows; + let index = flowList.findIndex(flow => flow.uniqueId === flowDeleteData.uniqueId); + expect(index).toBe(-1); + done(); + }, 50); + }); + +}); + diff --git a/openecomp-ui/test/licenseModel/entitlementPools/test.js b/openecomp-ui/test/licenseModel/entitlementPools/test.js new file mode 100644 index 0000000000..32705385b4 --- /dev/null +++ b/openecomp-ui/test/licenseModel/entitlementPools/test.js @@ -0,0 +1,244 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import deepFreeze from 'deep-freeze'; +import {expect} from 'chai'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js'; + +describe('Entitlement Pools Module Tests', function () { + + const LICENSE_MODEL_ID = '555'; + + it('Load Entitlement Pools List', () => { + const entitlementPoolsList = [ + { + name: 'ep1', + description: 'string', + thresholdValue: 75, + thresholdUnits: '%', + entitlementMetric: {'choice': 'User', 'other': ''}, + increments: 'string', + aggregationFunction: {'choice': 'Average', 'other': ''}, + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + time: {'choice': 'Hour', 'other': ''}, + sku: 'DEF2-385A-4521-AAAA', + id: '1', + referencingFeatureGroups: [], + partNumber: '51529' + } + ]; + deepFreeze(entitlementPoolsList); + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolsList', entitlementPoolsList); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/entitlement-pools`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return {results: entitlementPoolsList}; + }); + + return EntitlementPoolsActionHelper.fetchEntitlementPoolsList(store.dispatch, {licenseModelId: LICENSE_MODEL_ID}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Delete Entitlement Pool', () => { + const entitlementPoolsList = [ + { + name: 'ep1', + description: 'string', + thresholdValue: 75, + thresholdUnits: '%', + entitlementMetric: {'choice': 'User', 'other': ''}, + increments: 'string', + aggregationFunction: {'choice': 'Average', 'other': ''}, + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + time: {'choice': 'Hour', 'other': ''}, + sku: 'DEF2-385A-4521-AAAA', + id: '1', + referencingFeatureGroups: [], + partNumber: '51529' + } + ]; + + deepFreeze(entitlementPoolsList); + const store = storeCreator({ + licenseModel: { + entitlementPool: { + entitlementPoolsList + } + } + }); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolsList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/entitlement-pools/${entitlementPoolsList[0].id}`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return { + results: { + returnCode: 'OK' + } + }; + }); + + return EntitlementPoolsActionHelper.deleteEntitlementPool(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + entitlementPoolId: entitlementPoolsList[0].id + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add Entitlement Pool', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const entitlementPoolPostRequest = { + name: 'ep1', + description: 'string', + thresholdValue: 75, + thresholdUnits: '%', + entitlementMetric: {'choice': 'User', 'other': ''}, + increments: 'string', + aggregationFunction: {'choice': 'Average', 'other': ''}, + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + time: {'choice': 'Hour', 'other': ''}, + manufacturerReferenceNumber: 'DEF2-385A-4521-AAAA', + }; + const entitlementPoolToAdd = { + name: 'ep1', + description: 'string', + thresholdValue: 75, + thresholdUnits: '%', + entitlementMetric: {'choice': 'User', 'other': ''}, + increments: 'string', + aggregationFunction: {'choice': 'Average', 'other': ''}, + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + time: {'choice': 'Hour', 'other': ''}, + manufacturerReferenceNumber: 'DEF2-385A-4521-AAAA', + referencingFeatureGroups: [] + }; + const entitlementPoolIdFromResponse = 'ADDED_ID'; + const entitlementPoolAfterAdd = { + ...entitlementPoolToAdd, + id: entitlementPoolIdFromResponse + }; + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolsList', [entitlementPoolAfterAdd]); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/entitlement-pools`); + expect(data).to.deep.equal(entitlementPoolPostRequest); + expect(options).to.equal(undefined); + return { + returnCode: 'OK', + value: entitlementPoolIdFromResponse + }; + }); + + return EntitlementPoolsActionHelper.saveEntitlementPool(store.dispatch, + { + licenseModelId: LICENSE_MODEL_ID, + previousEntitlementPool: null, + entitlementPool: entitlementPoolToAdd + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update Entitlement Pool', () => { + const entitlementPoolsList = [{ + name: 'ep1', + id: '0', + description: 'string', + thresholdValue: 75, + thresholdUnits: '%', + entitlementMetric: {'choice': 'User', 'other': ''}, + increments: 'string', + aggregationFunction: {'choice': 'Average', 'other': ''}, + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + time: {'choice': 'Hour', 'other': ''}, + manufacturerReferenceNumber: 'DEF2-385A-4521-AAAA' + }]; + deepFreeze(entitlementPoolsList); + + const store = storeCreator({ + licenseModel: { + entitlementPool: { + entitlementPoolsList + } + } + }); + deepFreeze(store.getState()); + + const toBeUpdatedEntitlementPoolId = entitlementPoolsList[0].id; + const previousEntitlementPoolData = entitlementPoolsList[0]; + const entitlementPoolUpdateData = { + ...entitlementPoolsList[0], + name: 'ep1_UPDATED', + description: 'string_UPDATED' + }; + deepFreeze(entitlementPoolUpdateData); + + const entitlementPoolPutRequest = { + name: 'ep1_UPDATED', + description: 'string_UPDATED', + thresholdValue: 75, + thresholdUnits: '%', + entitlementMetric: {'choice': 'User', 'other': ''}, + increments: 'string', + aggregationFunction: {'choice': 'Average', 'other': ''}, + operationalScope: {'choices': ['Other'], 'other': 'blabla'}, + time: {'choice': 'Hour', 'other': ''}, + manufacturerReferenceNumber: 'DEF2-385A-4521-AAAA' + }; + deepFreeze(entitlementPoolPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.entitlementPool.entitlementPoolsList', [entitlementPoolUpdateData]); + + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/entitlement-pools/${toBeUpdatedEntitlementPoolId}`); + expect(data).to.deep.equal(entitlementPoolPutRequest); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return EntitlementPoolsActionHelper.saveEntitlementPool(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + previousEntitlementPool: previousEntitlementPoolData, + entitlementPool: entitlementPoolUpdateData + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + +}); diff --git a/openecomp-ui/test/licenseModel/featureGroups/test.js b/openecomp-ui/test/licenseModel/featureGroups/test.js new file mode 100644 index 0000000000..d334ab758e --- /dev/null +++ b/openecomp-ui/test/licenseModel/featureGroups/test.js @@ -0,0 +1,212 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js'; + + +describe('Feature Groups Module Tests', function () { + + const LICENSE_MODEL_ID = '555'; + + it('Load Feature Groups List', () => { + const featureGroupsList = [ + { + name: 'fs1', + id: 0, + description: 'fs1-d', + licenseKeyGroupsIds: [1], + entitlementPoolsIds: [1], + refCount: 0 + } + ]; + deepFreeze(featureGroupsList); + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.featureGroup.featureGroupsList', featureGroupsList); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/feature-groups`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return {results: featureGroupsList}; + }); + + return FeatureGroupsActionHelper.fetchFeatureGroupsList(store.dispatch, {licenseModelId: LICENSE_MODEL_ID}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Delete Feature Group', () => { + const featureGroupsList = [ + { + name: 'fs1', + id: 0, + description: 'fs1-d', + licenseKeyGroupsIds: [1], + entitlementPoolsIds: [1], + refCount: 0 + } + ]; + deepFreeze(featureGroupsList); + const store = storeCreator({ + licenseModel: { + featureGroup: { + featureGroupsList + } + } + }); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.featureGroup.featureGroupsList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/feature-groups/${featureGroupsList[0].id}`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return { + results: { + returnCode: 'OK' + } + }; + }); + + return FeatureGroupsActionHelper.deleteFeatureGroup(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + featureGroupId: featureGroupsList[0].id + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add Feature Group', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const featureGroupPostRequest = { + name: 'fs1', + description: 'fs1-d', + partNumber: '123', + addedLicenseKeyGroupsIds: [1], + addedEntitlementPoolsIds: [1] + }; + const featureGroupToAdd = { + name: 'fs1', + description: 'fs1-d', + partNumber: '123', + licenseKeyGroupsIds: [1], + entitlementPoolsIds: [1] + }; + const featureGroupIdFromResponse = 'ADDED_ID'; + const featureGroupAfterAdd = { + ...featureGroupToAdd, + id: featureGroupIdFromResponse + }; + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.featureGroup.featureGroupsList', [featureGroupAfterAdd]); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/feature-groups`); + expect(data).to.deep.equal(featureGroupPostRequest); + expect(options).to.equal(undefined); + return { + returnCode: 'OK', + value: featureGroupIdFromResponse + }; + }); + + return FeatureGroupsActionHelper.saveFeatureGroup(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + featureGroup: featureGroupToAdd + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update Feature Group', () => { + const featureGroupsList = [{ + name: 'fs1', + id: 0, + description: 'fs1-d', + partNumber: '123', + licenseKeyGroupsIds: [1], + entitlementPoolsIds: [1], + refCount: 0 + }]; + deepFreeze(featureGroupsList); + + const store = storeCreator({ + licenseModel: { + featureGroup: { + featureGroupsList + } + } + }); + deepFreeze(store.getState()); + + const toBeUpdatedFeatureGroupId = featureGroupsList[0].id; + const previousFeatureGroupData = featureGroupsList[0]; + const featureGroupUpdateData = { + ...featureGroupsList[0], + name: 'fs_UPDATED', + description: 'description_UPDATED', + partNumber: '123_UPDATED', + licenseKeyGroupsIds: [7], + entitlementPoolsIds: [7] + }; + deepFreeze(featureGroupUpdateData); + + const featureGroupPutRequest = { + name: 'fs_UPDATED', + description: 'description_UPDATED', + partNumber: '123_UPDATED', + addedLicenseKeyGroupsIds: [7], + addedEntitlementPoolsIds: [7], + removedLicenseKeyGroupsIds: [1], + removedEntitlementPoolsIds: [1] + }; + deepFreeze(featureGroupPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.featureGroup.featureGroupsList', [featureGroupUpdateData]); + + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/feature-groups/${toBeUpdatedFeatureGroupId}`); + expect(data).to.deep.equal(featureGroupPutRequest); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return FeatureGroupsActionHelper.saveFeatureGroup(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + previousFeatureGroup: previousFeatureGroupData, + featureGroup: featureGroupUpdateData + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + +}); diff --git a/openecomp-ui/test/licenseModel/licenseAgreement/test.js b/openecomp-ui/test/licenseModel/licenseAgreement/test.js new file mode 100644 index 0000000000..a6e8a3d363 --- /dev/null +++ b/openecomp-ui/test/licenseModel/licenseAgreement/test.js @@ -0,0 +1,205 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js'; + + +describe('License Agreement Module Tests', () => { + + const LICENSE_MODEL_ID = '777'; + + it('Load License Agreement List', () => { + const licenseAgreementList = [ + { + id: '0', + name: 'name0', + description: 'description0', + licenseTerm: 'licenseTerm0', + requirementsAndConstrains: 'req_and_constraints0', + featureGroupsIds: ['77'] + } + ]; + deepFreeze(licenseAgreementList); + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseAgreement.licenseAgreementList', licenseAgreementList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-agreements`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return {results: licenseAgreementList}; + }); + return LicenseAgreementActionHelper.fetchLicenseAgreementList(store.dispatch, {licenseModelId: LICENSE_MODEL_ID}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Delete License Agreement', () => { + const licenseAgreementList = [ + { + id: '0', + name: 'name0', + description: 'description0', + licenseTerm: 'licenseTerm0', + requirementsAndConstrains: 'req_and_constraints0', + featureGroupsIds: ['77'] + } + ]; + deepFreeze(licenseAgreementList); + const store = storeCreator({ + licenseModel: { + licenseAgreement: { + licenseAgreementList + } + } + }); + deepFreeze(store.getState()); + const toBeDeletedLicenseAgreementId = '0'; + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseAgreement.licenseAgreementList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-agreements/${toBeDeletedLicenseAgreementId}`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + }); + + return LicenseAgreementActionHelper.deleteLicenseAgreement(store.dispatch, { + licenseAgreementId: toBeDeletedLicenseAgreementId, + licenseModelId: LICENSE_MODEL_ID + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add License Agreement', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + const licenseAgreementPostRequest = { + name: 'name_ADDED_LA', + description: 'description_ADDED_LA', + licenseTerm: 'licenseTerm_ADDED_LA', + requirementsAndConstrains: 'req_and_constraints_ADDED_LA', + addedFeatureGroupsIds: [] + }; + deepFreeze(licenseAgreementPostRequest); + + const licenseAgreementToAdd = { + name: 'name_ADDED_LA', + description: 'description_ADDED_LA', + licenseTerm: 'licenseTerm_ADDED_LA', + requirementsAndConstrains: 'req_and_constraints_ADDED_LA', + featureGroupsIds: [] + }; + deepFreeze(licenseAgreementToAdd); + + const licenseAgreementIdFromResponse = 'ADDED_ID'; + const licenseAgreementAfterAdd = { + ...licenseAgreementToAdd, + id: licenseAgreementIdFromResponse + }; + deepFreeze(licenseAgreementAfterAdd); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseAgreement.licenseAgreementList', [licenseAgreementAfterAdd]); + + mockRest.addHandler('create', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-agreements`); + expect(data).to.deep.equal(licenseAgreementPostRequest); + expect(options).to.equal(undefined); + return { + value: licenseAgreementIdFromResponse + }; + }); + + return LicenseAgreementActionHelper.saveLicenseAgreement(store.dispatch, { + licenseAgreement: licenseAgreementToAdd, + licenseModelId: LICENSE_MODEL_ID + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update License Agreement', () => { + const licenseAgreementList = [ + { + id: '0', + name: 'name0', + description: 'description0', + licenseTerm: 'licenseTerm0', + requirementsAndConstrains: 'req_and_constraints0', + featureGroupsIds: ['77'] + } + ]; + const store = storeCreator({ + licenseModel: { + licenseAgreement: { + licenseAgreementList + } + } + }); + deepFreeze(store.getState()); + + const toBeUpdatedLicenseAgreementId = licenseAgreementList[0].id; + const previousLicenseAgreementData = licenseAgreementList[0]; + + const licenseAgreementUpdateData = { + ...licenseAgreementList[0], + name: 'name_UPDATED', + description: 'description_UPDATED', + licenseTerm: 'licenseTerm_UPDATED_LA', + requirementsAndConstrains: 'req_and_constraints_UPDATED_LA', + featureGroupsIds: ['update_id_1', 'update_id_2'] + }; + deepFreeze(licenseAgreementUpdateData); + + const licenseAgreementPutRequest = { + name: 'name_UPDATED', + description: 'description_UPDATED', + licenseTerm: 'licenseTerm_UPDATED_LA', + requirementsAndConstrains: 'req_and_constraints_UPDATED_LA', + addedFeatureGroupsIds: ['update_id_1', 'update_id_2'], + removedFeatureGroupsIds: ['77'] + }; + deepFreeze(licenseAgreementPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseAgreement.licenseAgreementList', [licenseAgreementUpdateData]); + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-agreements/${toBeUpdatedLicenseAgreementId}`); + expect(data).to.deep.equal(licenseAgreementPutRequest); + expect(options).to.equal(undefined); + }); + + return LicenseAgreementActionHelper.saveLicenseAgreement(store.dispatch, { + licenseModelId: LICENSE_MODEL_ID, + previousLicenseAgreement: previousLicenseAgreementData, + licenseAgreement: licenseAgreementUpdateData + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + +}); diff --git a/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js b/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js new file mode 100644 index 0000000000..944bd44e49 --- /dev/null +++ b/openecomp-ui/test/licenseModel/licenseKeyGroups/test.js @@ -0,0 +1,197 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; + +import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js'; + +describe('License Key Groups Module Tests', function () { + + const LICENSE_MODEL_ID = '555'; + it('Load License Key Group', () => { + const licenseKeyGroupsList = [ + { + name: 'lsk1', + description: 'string', + type: 'Unique', + operationalScope: {'choices': ['Data_Center'], 'other': ''}, + id: '0' + } + ]; + deepFreeze(licenseKeyGroupsList); + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsList', licenseKeyGroupsList); + + mockRest.addHandler('fetch', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-key-groups`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return {results: licenseKeyGroupsList}; + }); + + return LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(store.dispatch, {licenseModelId: LICENSE_MODEL_ID}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Delete License Key Group', () => { + const licenseKeyGroupsList = [ + { + name: 'lsk1', + description: 'string', + type: 'Unique', + operationalScope: {'choices': ['Data_Center'], 'other': ''}, + id: '0' + } + ]; + deepFreeze(licenseKeyGroupsList); + const store = storeCreator({ + licenseModel: { + licenseKeyGroup: { + licenseKeyGroupsList + } + } + }); + deepFreeze(store.getState()); + const toBeDeletedLicenseKeyGroupId = '0'; + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-key-groups/${toBeDeletedLicenseKeyGroupId}`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + }); + + return LicenseKeyGroupsActionHelper.deleteLicenseKeyGroup(store.dispatch, { + licenseKeyGroupId: toBeDeletedLicenseKeyGroupId, + licenseModelId: LICENSE_MODEL_ID + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add License Key Group', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const licenseKeyGroupPostRequest = { + name: 'lsk1_ADDED', + description: 'string_ADDED', + type: 'Unique_ADDED', + operationalScope: {'choices': ['Data_Center'], 'other': ''} + }; + deepFreeze(licenseKeyGroupPostRequest); + + const licenseKeyGroupToAdd = { + ...licenseKeyGroupPostRequest + }; + + deepFreeze(licenseKeyGroupToAdd); + + const licenseKeyGroupIdFromResponse = 'ADDED_ID'; + const licenseKeyGroupAfterAdd = { + ...licenseKeyGroupToAdd, + id: licenseKeyGroupIdFromResponse + }; + deepFreeze(licenseKeyGroupAfterAdd); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsList', [licenseKeyGroupAfterAdd]); + + mockRest.addHandler('create', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-key-groups`); + expect(data).to.deep.equal(licenseKeyGroupPostRequest); + expect(options).to.equal(undefined); + return { + value: licenseKeyGroupIdFromResponse + }; + }); + + return LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(store.dispatch, { + licenseKeyGroup: licenseKeyGroupToAdd, + licenseModelId: LICENSE_MODEL_ID + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update License Key Group', () => { + const licenseKeyGroupsList = [ + { + name: 'lsk1', + description: 'string', + type: 'Unique', + operationalScope: {'choices': ['Data_Center'], 'other': ''}, + id: '0' + } + ]; + deepFreeze(licenseKeyGroupsList); + const store = storeCreator({ + licenseModel: { + licenseKeyGroup: { + licenseKeyGroupsList + } + } + }); + + const toBeUpdatedLicenseKeyGroupId = licenseKeyGroupsList[0].id; + const previousLicenseKeyGroupData = licenseKeyGroupsList[0]; + + const licenseKeyGroupUpdateData = { + ...licenseKeyGroupsList[0], + name: 'lsk1_UPDATE', + description: 'string_UPDATE', + type: 'Unique', + operationalScope: {'choices': ['Data_Center'], 'other': ''} + }; + deepFreeze(licenseKeyGroupUpdateData); + + const licenseKeyGroupPutRequest = { + name: 'lsk1_UPDATE', + description: 'string_UPDATE', + type: 'Unique', + operationalScope: {'choices': ['Data_Center'], 'other': ''} + }; + deepFreeze(licenseKeyGroupPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModel.licenseKeyGroup.licenseKeyGroupsList', [licenseKeyGroupUpdateData]); + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-license-models/${LICENSE_MODEL_ID}/license-key-groups/${toBeUpdatedLicenseKeyGroupId}`); + expect(data).to.deep.equal(licenseKeyGroupPutRequest); + expect(options).to.equal(undefined); + }); + + return LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(store.dispatch, { + previousLicenseKeyGroup: previousLicenseKeyGroupData, + licenseKeyGroup: licenseKeyGroupUpdateData, + licenseModelId: LICENSE_MODEL_ID + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + +}); diff --git a/openecomp-ui/test/licenseModel/test.js b/openecomp-ui/test/licenseModel/test.js new file mode 100644 index 0000000000..c21d18f146 --- /dev/null +++ b/openecomp-ui/test/licenseModel/test.js @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import LicenseModelCreationActionHelper from 'sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js'; + +describe('License Model Module Tests', function () { + it('Add License Model', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const licenseModelPostRequest = deepFreeze({ + vendorName: 'vlm1', + description: 'string', + iconRef: 'icon' + }); + + const licenseModelToAdd = deepFreeze({ + ...licenseModelPostRequest + }); + + const licenseModelIdFromResponse = 'ADDED_ID'; + const licenseModelAfterAdd = deepFreeze({ + ...licenseModelToAdd, + id: licenseModelIdFromResponse + }); + + const expectedStore = cloneAndSet(store.getState(), 'licenseModelList', [licenseModelAfterAdd]); + + mockRest.addHandler('create', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal('/onboarding-api/v1.0/vendor-license-models/'); + expect(data).to.deep.equal(licenseModelPostRequest); + expect(options).to.equal(undefined); + return { + value: licenseModelIdFromResponse + }; + }); + + return LicenseModelCreationActionHelper.createLicenseModel(store.dispatch, { + licenseModel: licenseModelToAdd + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); +}); diff --git a/openecomp-ui/test/nfvo-components/input/dualListBox/dualListbox.test.js b/openecomp-ui/test/nfvo-components/input/dualListBox/dualListbox.test.js new file mode 100644 index 0000000000..eaa06eedf4 --- /dev/null +++ b/openecomp-ui/test/nfvo-components/input/dualListBox/dualListbox.test.js @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx'; + +const ITEMS = [{id: '1', name: 'aaa'}, {id: '2', name: 'bbb'}, {id: '3', name: 'ccc'}]; + +describe('dualListBox Module Tests', function () { + + + it('should render basically', () => { + var renderer = TestUtils.createRenderer(); + renderer.render({}}/>); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('should render with available list and 4 control buttons', () => { + var view = TestUtils.renderIntoDocument({}}/>); + expect(view).toExist(); + var results = TestUtils.scryRenderedDOMComponentsWithClass(view, 'dual-list-option'); + expect(results.length).toBe(4); + }); + + it('should add item to selected list', done => { + const newItemValue = 'new item'; + let onChange = (value)=> { + expect(value).toEqual(newItemValue); + done(); + }; + var view = new DualListboxView({availableList:ITEMS, onChange, selectedValuesList:[]}); + expect(view).toExist(); + view.refs = { + availableValues: {getValue(){return newItemValue;}} + }; + view.addToSelectedList(); + }); + + it('should remove item from selected list', done => { + const selectedValuesList = ['a','b']; + let onChange = (value)=> { + expect(value).toEqual(selectedValuesList[1]); + done(); + }; + var view = new DualListboxView({availableList:ITEMS, onChange, selectedValuesList}); + expect(view).toExist(); + view.refs = { + selectedValues: {getValue(){return ['a'];}} + }; + view.removeFromSelectedList(); + }); + + it('should add all items to selected list', done => { + let onChange = (value)=> { + expect(value).toEqual(ITEMS.map(item => item.id)); + done(); + }; + var view = new DualListboxView({availableList:ITEMS, onChange, selectedValuesList:[]}); + expect(view).toExist(); + view.addAllToSelectedList(); + }); + + it('should remove all items from selected list', done => { + let onChange = (value)=> { + expect(value.length).toBe(0); + done(); + }; + var view = new DualListboxView({availableList:ITEMS, onChange, selectedValuesList:[]}); + expect(view).toExist(); + view.removeAllFromSelectedList(); + }); + + +}); diff --git a/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js b/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js new file mode 100644 index 0000000000..a3b098f611 --- /dev/null +++ b/openecomp-ui/test/nfvo-components/listEditor/listEditor.test.js @@ -0,0 +1,96 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import ListEditorView from 'src/nfvo-components/listEditor/ListEditorView.jsx'; +import ListEditorItemView from 'src/nfvo-components/listEditor/ListEditorItemView.jsx'; + +describe('listEditor Module Tests', function () { + + + it('list editor view should exist', () => { + expect(ListEditorView).toExist(); + }); + + it('list editor item view should exist', () => { + expect(ListEditorItemView).toExist(); + }); + + it('should render list and list item and call onEdit', done => { + let itemView = TestUtils.renderIntoDocument( + + +
      +
      +
      + ); + expect(itemView).toExist(); + let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, 'fa-sliders'); + TestUtils.Simulate.click(sliderIcon); + }); + + it('should render list and list item and call onFilter', done => { + let itemView = TestUtils.renderIntoDocument( + {done();}}> + +
      +
      +
      + ); + expect(itemView).toExist(); + let filterInput = TestUtils.findRenderedDOMComponentWithTag(itemView, 'input'); + TestUtils.Simulate.change(filterInput); + }); + + it('should render READONLY list item and not call onEdit', done => { + let itemView = TestUtils.renderIntoDocument( + +
      +
      + ); + expect(itemView).toExist(); + let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, 'fa-sliders'); + TestUtils.Simulate.click(sliderIcon); + }); + + it('should render list item and call onDelete', done => { + let itemView = TestUtils.renderIntoDocument( + +
      +
      + ); + expect(itemView).toExist(); + let sliderIcon = TestUtils.findRenderedDOMComponentWithClass(itemView, 'fa-trash-o'); + TestUtils.Simulate.click(sliderIcon); + }); + + it('should render READONLY list item and not call onDelete', () => { + let itemView = TestUtils.renderIntoDocument( + {}} isReadOnlyMode={true}> +
      +
      + ); + expect(itemView).toExist(); + let sliderIcon = TestUtils.scryRenderedDOMComponentsWithClass(itemView, 'fa-trash-o'); + expect(sliderIcon).toEqual(0); + }); +}); diff --git a/openecomp-ui/test/nfvo-components/notifications/notificationsModal.test.js b/openecomp-ui/test/nfvo-components/notifications/notificationsModal.test.js new file mode 100644 index 0000000000..f84d38246d --- /dev/null +++ b/openecomp-ui/test/nfvo-components/notifications/notificationsModal.test.js @@ -0,0 +1,144 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import store from 'sdc-app/AppStore.js'; +import ConnectedNotificationModal, {NotificationModal} from 'nfvo-components/notifications/NotificationModal.jsx'; +import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; + +const title = 'test title'; +const msg = 'test msg'; + +describe('Notification Modal Mapper and View Class: ', function () { + + it('notification should show with type error', done => { + store.dispatch({type: NotificationConstants.NOTIFY_ERROR, data: {title, msg}}); + setTimeout(()=> { + expect(store.getState().notification).toExist(); + expect(store.getState().notification.type).toBe('error'); + done(); + }, 0); + }); + + it('notification should show with type default', done => { + store.dispatch({type: NotificationConstants.NOTIFY_INFO, data: {title, msg}}); + setTimeout(()=> { + expect(store.getState().notification).toExist(); + expect(store.getState().notification.type).toBe('default'); + done(); + }, 0); + }); + + it('notification should show with type warning', done => { + store.dispatch({type: NotificationConstants.NOTIFY_WARNING, data: {title, msg}}); + setTimeout(()=> { + expect(store.getState().notification).toExist(); + expect(store.getState().notification.type).toBe('warning'); + done(); + }, 0); + }); + + it('notification should show with type success', done => { + store.dispatch({type: NotificationConstants.NOTIFY_SUCCESS, data: {title, msg}}); + setTimeout(()=> { + expect(store.getState().notification).toExist(); + expect(store.getState().notification.type).toBe('success'); + done(); + }, 0); + }); + + it('notification should show with type success with connected component', done => { + store.dispatch({type: NotificationConstants.NOTIFY_SUCCESS, data: {title, msg}}); + setTimeout(()=> { + expect(store.getState().notification).toExist(); + expect(store.getState().notification.type).toBe('success'); + let renderer = TestUtils.createRenderer(); + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + done(); + }, 0); + }); + + it('notification should hide with connected component', done => { + setTimeout(()=> { + expect(store.getState().notification).toNotExist(); + let renderer = TestUtils.createRenderer(); + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + done(); + }, 0); + store.dispatch({type: NotificationConstants.NOTIFY_CLOSE}); + }); + + it('notification should hide', done => { + store.dispatch({type: NotificationConstants.NOTIFY_CLOSE}); + setTimeout(()=> { + expect(store.getState().notification).toNotExist(); + done(); + }, 0); + }); + + it('NotificationModal should not render', ()=> { + let renderer = TestUtils.createRenderer(); + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('NotificationModal basic default render', ()=> { + expect(window.document).toExist(); + let document = TestUtils.renderIntoDocument( + {}}/> + ); + var result = TestUtils.findAllInRenderedTree(document, element => element.props.className === 'notification-modal primary'); + expect(result.length).toBeGreaterThan(0); + }); + + it('NotificationModal basic error render', ()=> { + expect(window.document).toExist(); + let document = TestUtils.renderIntoDocument( + {}}/> + ); + var result = TestUtils.findAllInRenderedTree(document, element => element.props.className === 'notification-modal danger'); + expect(result.length).toBeGreaterThan(0); + }); + + it('NotificationModal basic warning render', ()=> { + expect(window.document).toExist(); + let document = TestUtils.renderIntoDocument( + {}}/> + ); + var result = TestUtils.findAllInRenderedTree(document, element => element.props.className === 'notification-modal warning'); + expect(result.length).toBeGreaterThan(0); + }); + + it('NotificationModal basic success render', ()=> { + expect(window.document).toExist(); + let document = TestUtils.renderIntoDocument( + {}}/> + ); + var result = TestUtils.findAllInRenderedTree(document, element => element.props.className === 'notification-modal success'); + expect(result.length).toBeGreaterThan(0); + }); +}); diff --git a/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js b/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js new file mode 100644 index 0000000000..9ab18137cf --- /dev/null +++ b/openecomp-ui/test/nfvo-components/panel/VersionController/versionController.test.js @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import VersionController from 'nfvo-components/panel/versionController/VersionController.jsx'; +import {actionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +describe('versionController UI Component', () => { + + it('function does exist', () => { + var renderer = TestUtils.createRenderer(); + renderer.render(); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('validating checkin function', () => { + + let versionController = TestUtils.renderIntoDocument({return Promise.resolve();}}/>); + let cb = action => expect(action).toBe(actionsEnum.CHECK_IN); + versionController.checkin(cb); + + }); + +}); diff --git a/openecomp-ui/test/nfvo-components/panel/VersionController/versionControllerUtils.test.js b/openecomp-ui/test/nfvo-components/panel/VersionController/versionControllerUtils.test.js new file mode 100644 index 0000000000..0e4a92118e --- /dev/null +++ b/openecomp-ui/test/nfvo-components/panel/VersionController/versionControllerUtils.test.js @@ -0,0 +1,172 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import deepFreeze from 'deep-freeze'; +import Configuration from 'sdc-app/config/Configuration.js'; +import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js'; +import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +const status = 'testStatus'; + +describe('versionController UI Component', () => { + + it('function does exist', () => { + expect(VersionControllerUtils).toExist(); + }); + + it('validating getCheckOutStatusKindByUserID - without "UserID"', () => { + var result = VersionControllerUtils.getCheckOutStatusKindByUserID(status); + expect(result.status).toBe(status); + expect(result.isCheckedOut).toBe(true); + }); + + it('validating getCheckOutStatusKindByUserID - without "UserID" with locking user', () => { + var result = VersionControllerUtils.getCheckOutStatusKindByUserID(status, 'locking user'); + expect(result.status).toBe(statusEnum.LOCK_STATUS); + expect(result.isCheckedOut).toBe(false); + }); + + it('validating getCheckOutStatusKindByUserID - with "UserID" with configuration set', () => { + const userId = 'att'; + + Configuration.set('ATTUserID', userId); + var result = VersionControllerUtils.getCheckOutStatusKindByUserID(status, userId); + Configuration.set('ATTUserID', undefined); + + expect(result.status).toBe(status); + expect(result.isCheckedOut).toBe(true); + }); + + + + it('validating isCheckedOutByCurrentUser - when resource is not checked out', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.6', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Final' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isCheckedOutByCurrentUser(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(false); + }); + + it('validating isCheckedOutByCurrentUser - when resource is checked out', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.6', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Locked', + lockingUser: 'current' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isCheckedOutByCurrentUser(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(true); + }); + + it('validating isCheckedOutByCurrentUser - when resource is checked out by another user', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.6', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Locked', + lockingUser: 'another' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isCheckedOutByCurrentUser(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(false); + }); + + + + it('validating isReadOnly - when resource is not checked out', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.6', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Final' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isReadOnly(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(true); + }); + + it('validating isReadOnly - when resource is checked out', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.6', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Locked', + lockingUser: 'current' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isReadOnly(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(false); + }); + + it('validating isReadOnly - when version of resource is not latest', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.2', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Locked', + lockingUser: 'current' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isReadOnly(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(true); + }); + + it('validating isReadOnly - when resource is checked out by another user', () => { + const currentUser = 'current'; + const resource = deepFreeze({ + version: '0.6', + viewableVersions: ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6'], + status: 'Locked', + lockingUser: 'another' + }); + + Configuration.set('ATTUserID', currentUser); + const result = VersionControllerUtils.isReadOnly(resource); + Configuration.set('ATTUserID', undefined); + + expect(result).toBe(true); + }); +}); + diff --git a/openecomp-ui/test/setup.test.js b/openecomp-ui/test/setup.test.js new file mode 100644 index 0000000000..72f8b954b8 --- /dev/null +++ b/openecomp-ui/test/setup.test.js @@ -0,0 +1,25 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import mockRest from 'test-utils/MockRest.js'; + +beforeEach(function() { + mockRest.resetQueue(); +}); diff --git a/openecomp-ui/test/softwareProduct/attachments/SoftwareProductAttachmentsView.test.js b/openecomp-ui/test/softwareProduct/attachments/SoftwareProductAttachmentsView.test.js new file mode 100644 index 0000000000..839176c970 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/attachments/SoftwareProductAttachmentsView.test.js @@ -0,0 +1,198 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js'; + +import SoftwareProductAttachmentsView from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + + +describe('SoftwareProductAttachments Modal Mapper and View Classes', () => { + + it ('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + + it ('mapStateToProps check data', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + const atTree = { + 'children': [ + { + 'name': 'HEAT', + 'expanded': true, + 'type': 'heat', + 'children': [ + { + 'name': 'heat_zxeyCtMHhf2.yaml', + 'expanded': true, + 'type': 'heat', + 'errors': [ + { + 'level': 'WARNING', + 'message': 'Resource is not defined as output and thus cannot be Shared. resource id - network_4' + } + ], + 'children': [ + { + 'name': 'heat_env_zxeyCtMHhf2.env', + 'type': 'env' + } + ] + } + ] + } + ] + }; + const errorList = [ + { + 'errorLevel': 'WARNING', + 'errorMessage': 'Resource is not defined as output and thus cannot be Shared. resource id - network_4', + 'name': 'heat_zxeyCtMHhf2.yaml', + 'hasParent': false, + 'parentName': 'HEAT', + 'type': 'heat' + }, + { + 'errorLevel': 'WARNING', + 'errorMessage': 'Resource is not defined as output and thus cannot be Shared. resource id - network_3', + 'name': 'heat_zxeyCtMHhf2.yaml', + 'hasParent': false, + 'parentName': 'HEAT', + 'type': 'heat' + } + ]; + + var obj = { + softwareProduct: { + softwareProductEditor: { + data:currentSoftwareProduct + }, softwareProductAttachments: + { + attachmentsTree: atTree, + errorList: errorList + } + } + }; + + var results = mapStateToProps(obj); + expect(results.attachmentsTree).toExist(); + expect(results.errorList).toExist(); + expect(results.hoveredNode).toBe(undefined); + expect(results.selectedNode).toBe(undefined); + }); + + + it('function does exist', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + const versionControllerData = { + version: currentSoftwareProduct.version, + viewableVersions:currentSoftwareProduct.viewableVersions, + status: currentSoftwareProduct.status, + isCheckedOut: true + }; + const atTree = { + 'children': [ + { + 'name': 'HEAT', + 'expanded': true, + 'type': 'heat', + 'children': [ + { + 'name': 'heat_zxeyCtMHhf2.yaml', + 'expanded': true, + 'type': 'heat', + 'errors': [ + { + 'level': 'WARNING', + 'message': 'Resource is not defined as output and thus cannot be Shared. resource id - network_4' + } + ], + 'children': [ + { + 'name': 'heat_env_zxeyCtMHhf2.env', + 'type': 'env' + } + ] + } + ] + } + ] + }; + const errorList = [ + { + 'errorLevel': 'WARNING', + 'errorMessage': 'Resource is not defined as output and thus cannot be Shared. resource id - network_4', + 'name': 'heat_zxeyCtMHhf2.yaml', + 'hasParent': false, + 'parentName': 'HEAT', + 'type': 'heat' + }, + { + 'errorLevel': 'WARNING', + 'errorMessage': 'Resource is not defined as output and thus cannot be Shared. resource id - network_3', + 'name': 'heat_zxeyCtMHhf2.yaml', + 'hasParent': false, + 'parentName': 'HEAT', + 'type': 'heat' + } + ]; + + + var renderer = TestUtils.createRenderer(); + renderer.render(); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + +}); diff --git a/openecomp-ui/test/softwareProduct/attachments/SoftwareproductAttachmentsHelper.test.js b/openecomp-ui/test/softwareProduct/attachments/SoftwareproductAttachmentsHelper.test.js new file mode 100644 index 0000000000..851560caa8 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/attachments/SoftwareproductAttachmentsHelper.test.js @@ -0,0 +1,153 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import SoftwareProductAttachmentsActionHelper from 'sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import deepFreeze from 'deep-freeze'; +import {actionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; + + + + + + +describe('SoftwareProductAttachments ActionHelper', () => { + + it('function does exist', () => { + expect(SoftwareProductAttachmentsActionHelper).toExist(); + }); + + it('toggleExpanded function check', () => { + + + const validationData = { + importStructure: { + HEAT: [ + { + fileName: 'hot-mog-0108-bs1271.yml', + env: { + fileName: 'hot-mog-0108-bs1271.env' + }, + errors: [ + { + 'level': 'WARNING', + 'message': 'Port not bind to any NOVA Server, Resource Id [sm02_port_2]' + }, + { + 'level': 'WARNING', + 'message': 'Port not bind to any NOVA Server, Resource Id [sm01_port_2]' + } + ] + } + ] + } + }; + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: 'Locked', + lockingUser: 'cs0008', + validationData + }; + + + const store = storeCreator(); + deepFreeze(store.getState()); + deepFreeze(currentSoftwareProduct); + + store.dispatch({ + type:actionTypes.SOFTWARE_PRODUCT_LOADED, + response: currentSoftwareProduct + }); + + expect(store.getState().softwareProduct.softwareProductAttachments.attachmentsTree.children[0].expanded).toBe(true); + SoftwareProductAttachmentsActionHelper.toggleExpanded(store.dispatch, {path:[0]}); + expect(store.getState().softwareProduct.softwareProductAttachments.attachmentsTree.children[0].expanded).toBe(false); + }); + + it('onSelectNode & onUnselectNode function check', () => { + + + const validationData = { + importStructure: { + HEAT: [ + { + fileName: 'hot-mog-0108-bs1271.yml', + env: { + fileName: 'hot-mog-0108-bs1271.env' + }, + errors: [ + { + 'level': 'WARNING', + 'message': 'Port not bind to any NOVA Server, Resource Id [sm02_port_2]' + }, + { + 'level': 'WARNING', + 'message': 'Port not bind to any NOVA Server, Resource Id [sm01_port_2]' + } + ] + } + ] + } + }; + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: 'Locked', + lockingUser: 'cs0008', + validationData + }; + + deepFreeze(currentSoftwareProduct); + + const store = storeCreator(); + deepFreeze(store.getState()); + + store.dispatch({ + type:actionTypes.SOFTWARE_PRODUCT_LOADED, + response: currentSoftwareProduct + }); + let expectedNodeName = 'name'; + expect(store.getState().softwareProduct.softwareProductAttachments.selectedNode).toBe(undefined); + SoftwareProductAttachmentsActionHelper.onSelectNode(store.dispatch, {nodeName:expectedNodeName}); + expect(store.getState().softwareProduct.softwareProductAttachments.selectedNode).toBe(expectedNodeName); + SoftwareProductAttachmentsActionHelper.onUnselectNode(store.dispatch); + expect(store.getState().softwareProduct.softwareProductAttachments.selectedNode).toBe(undefined); + }); + + +}); diff --git a/openecomp-ui/test/softwareProduct/components/compute/test.js b/openecomp-ui/test/softwareProduct/components/compute/test.js new file mode 100644 index 0000000000..925de302b8 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/compute/test.js @@ -0,0 +1,132 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; + +const softwareProductId = '123'; +const vspComponentId = '111'; + +describe('Software Product Components Compute Module Tests', function () { + + let restPrefix = ''; + + before(function() { + restPrefix = Configuration.get('restPrefix'); + deepFreeze(restPrefix); + }); + + it('Get Software Products Components Compute', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductComponentCompute = { + data: JSON.stringify({'vmSizing':{'numOfCPUs':'3','fileSystemSizeGB':'888'},'numOfVMs':{'minimum':'2'}}), + schema: JSON.stringify({'vmSizing':{'numOfCPUs':'3','fileSystemSizeGB':'888'},'numOfVMs':{'minimum':'2'}}) + }; + deepFreeze(softwareProductComponentCompute); + + const softwareProductComponentComputeData = { + qdata: JSON.parse(softwareProductComponentCompute.data), + qschema: JSON.parse(softwareProductComponentCompute.schema) + }; + deepFreeze(softwareProductComponentComputeData); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor', softwareProductComponentComputeData); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).toEqual(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${vspComponentId}/questionnaire`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return softwareProductComponentCompute; + }); + + return SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(store.dispatch, {softwareProductId, vspComponentId}).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Get Empty Software Products Components Compute', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductComponentQuestionnaire = { + data: null, + schema: JSON.stringify({'vmSizing':{'numOfCPUs':'3','fileSystemSizeGB':'888'},'numOfVMs':{'minimum':'2'}}) + }; + deepFreeze(softwareProductComponentQuestionnaire); + + const softwareProductComponentQuestionnaireData = { + qdata: {}, + qschema: JSON.parse(softwareProductComponentQuestionnaire.schema) + }; + deepFreeze(softwareProductComponentQuestionnaireData); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor', softwareProductComponentQuestionnaireData); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).toEqual(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${vspComponentId}/questionnaire`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return softwareProductComponentQuestionnaire; + }); + + return SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(store.dispatch, {softwareProductId, vspComponentId}).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Update Software Products Components Compute', () => { + const store = storeCreator({ + softwareProduct: { + softwareProductComponents: { + componentEditor: { + qdata: { + numOfCPUs: 3, + fileSystemSizeGB: 999 + }, + qschema: { + type: 'object', + properties: { + numOfCPUs: {type: 'number'}, + fileSystemSizeGB: {type: 'number'} + } + } + } + } + } + }); + deepFreeze(store); + + const data = {numOfCPUs: 5, fileSystemSizeGB: 300}; + deepFreeze(data); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor.qdata', data); + + SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(store.dispatch, {data}); + + expect(store.getState()).toEqual(expectedStore); + }); +}); diff --git a/openecomp-ui/test/softwareProduct/components/general/SoftwareProductComponentsGeneral.test.js b/openecomp-ui/test/softwareProduct/components/general/SoftwareProductComponentsGeneral.test.js new file mode 100644 index 0000000000..ce2152b29b --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/general/SoftwareProductComponentsGeneral.test.js @@ -0,0 +1,129 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js'; +import SoftwareProductComponentsGeneralView from 'sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + + +describe('SoftwareProductComponentsGeneral Mapper and View Classes', () => { + it('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + it('mapStateToProps data test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + var obj = { + softwareProduct: { + softwareProductEditor: { + data: currentSoftwareProduct + }, + softwareProductComponents: { + componentEditor: { + data: {}, + qdata: {}, + qschema: {} + } + } + } + }; + + var results = mapStateToProps(obj); + expect(results.componentData).toExist(); + expect(results.qdata).toExist(); + expect(results.qschema).toExist(); + }); + + + it('VSP Components general view test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + const softwareProductComponents = { + componentEditor: { + data: {}, + qdata: {}, + qschema: { + $schema: 'http://json-schema.org/draft-04/schema#', + type: 'object', + properties: { + general: { + type: 'object', + properties: {} + } + } + } + } + }; + + const versionControllerData = { + version: '1', + viewableVersions: [], + status: 'locked', + isCheckedOut: true + }; + + const componentData = { + name: '', + description: '' + }; + + var renderer = TestUtils.createRenderer(); + renderer.render( + ); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + + }); + +}); diff --git a/openecomp-ui/test/softwareProduct/components/loadBalancing/softwareProductComponentLoadbalancing.test.js b/openecomp-ui/test/softwareProduct/components/loadBalancing/softwareProductComponentLoadbalancing.test.js new file mode 100644 index 0000000000..69a93b69e1 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/loadBalancing/softwareProductComponentLoadbalancing.test.js @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js'; +import SoftwareProductComponentLoadBalancingView from 'sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + + +describe('SoftwareProductComponentLoadBalancing Mapper and View Classes', () => { + it('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + it('mapStateToProps data test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + var obj = { + softwareProduct: { + softwareProductEditor: { + data: currentSoftwareProduct + }, + softwareProductComponents: { + componentEditor: { + qdata: {}, + qschema: {} + } + } + } + }; + + var results = mapStateToProps(obj); + expect(results.qdata).toExist(); + expect(results.qschema).toExist(); + }); + + + it('VSP Components LoadBalancing view test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + const softwareProductComponents = { + componentEditor: { + qdata: {}, + qschema: { + $schema: 'http://json-schema.org/draft-04/schema#', + type: 'object', + properties: { + general: { + type: 'object', + properties: {} + } + } + } + } + }; + + const versionControllerData = { + version: '1', + viewableVersions: [], + status: 'locked', + isCheckedOut: true + }; + + var renderer = TestUtils.createRenderer(); + renderer.render( + ); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + + }); + +}); diff --git a/openecomp-ui/test/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.test.js b/openecomp-ui/test/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.test.js new file mode 100644 index 0000000000..2f1ea12c01 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.test.js @@ -0,0 +1,101 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js'; +import SoftwareProductComponentsMonitoringView from 'sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx'; + +describe('SoftwareProductComponentsMonitoring Module Tests', function () { + + it('should mapper exist', () => { + expect(mapStateToProps).toExist(); + }); + + it('should return empty file names', () => { + let softwareProduct = {softwareProductEditor: {data: {}}, softwareProductComponents: {monitoring: {}}}; + var results = mapStateToProps({softwareProduct}); + expect(results.trapFilename).toEqual(undefined); + expect(results.pollFilename).toEqual(undefined); + }); + + it('should return trap file name', () => { + const monitoring = { + trapFilename: '123' + }; + let softwareProduct = {softwareProductEditor: {data: {}}, softwareProductComponents: {monitoring}}; + var results = mapStateToProps({softwareProduct}); + expect(results.trapFilename).toEqual(monitoring.trapFilename); + expect(results.pollFilename).toEqual(undefined); + }); + + it('should return poll file names', () => { + const monitoring = { + pollFilename: '123' + }; + let softwareProduct = {softwareProductEditor: {data: {}}, softwareProductComponents: {monitoring}}; + var results = mapStateToProps({softwareProduct}); + expect(results.trapFilename).toEqual(undefined); + expect(results.pollFilename).toEqual(monitoring.pollFilename); + + let renderer = TestUtils.createRenderer(); + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('should return both file names', () => { + const monitoring = { + trapFilename: '1234', + trapFilename: '123' + }; + let softwareProduct = {softwareProductEditor: {data: {}}, softwareProductComponents: {monitoring}}; + var results = mapStateToProps({softwareProduct}); + expect(results.trapFilename).toEqual(monitoring.trapFilename); + expect(results.pollFilename).toEqual(monitoring.pollFilename); + + let renderer = TestUtils.createRenderer(); + renderer.render(); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('should change state to dragging', done => { + var view = TestUtils.renderIntoDocument(); + expect(view.state.dragging).toBe(false); + view.handleOnDragEnter(false); + setTimeout(()=> { + expect(view.state.dragging).toBe(true); + done(); + }, 100); + }); + + it('should not change state to dragging', done => { + var view = TestUtils.renderIntoDocument(); + expect(view.state.dragging).toBe(false); + view.handleOnDragEnter(true); + setTimeout(()=> { + expect(view.state.dragging).toBe(false); + done(); + }, 0); + }); + +}); diff --git a/openecomp-ui/test/softwareProduct/components/monitoring/test.js b/openecomp-ui/test/softwareProduct/components/monitoring/test.js new file mode 100644 index 0000000000..172db653e9 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/monitoring/test.js @@ -0,0 +1,215 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import mockRest from 'test-utils/MockRest.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import SoftwareProductComponentsMonitoringConstants from 'sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js'; +import SoftwareProductComponentsMonitoringActionHelper from 'sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js'; + +const softwareProductId = '123'; +const componentId = '123'; + +describe('Software Product Components Monitoring Module Tests', function () { + + let store; + + beforeEach(()=> { + store = storeCreator(); + }); + + + it('Fetch for existing files - no files', done => { + + let emptyResult = Object.freeze({ + snmpTrap: undefined, + snmpPoll: undefined + }); + + mockRest.addHandler('fetch', ({ baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp`); + return emptyResult; + }); + + SoftwareProductComponentsMonitoringActionHelper.fetchExistingFiles(store.dispatch, { + softwareProductId, + componentId + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual(emptyResult.snmpPoll); + expect(monitoring.trapFilename).toEqual(emptyResult.snmpTrap); + done(); + }, 0); + + }); + + it('Fetch for existing files - only snmp trap file exists', done => { + let response = Object.freeze({ + snmpTrap: 'asdfasdf', + snmpPoll: undefined + }); + + mockRest.addHandler('fetch', ({ baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp`); + return response; + }); + + SoftwareProductComponentsMonitoringActionHelper.fetchExistingFiles(store.dispatch, { + softwareProductId, + componentId + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual(response.snmpPoll); + expect(monitoring.trapFilename).toEqual(response.snmpTrap); + done(); + }, 0); + }); + + it('Fetch for existing files - only snmp poll file exists', done => { + let response = Object.freeze({ + snmpPoll: 'asdfasdf', + snmpTrap: undefined + }); + + mockRest.addHandler('fetch', ({baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp`); + return response; + }); + + SoftwareProductComponentsMonitoringActionHelper.fetchExistingFiles(store.dispatch, { + softwareProductId, + componentId + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual(response.snmpPoll); + expect(monitoring.trapFilename).toEqual(response.snmpTrap); + done(); + }, 0); + }); + + it('Fetch for existing files - both files exist', done => { + let response = Object.freeze({ + snmpPoll: 'asdfasdf', + snmpTrap: 'asdfgg' + }); + + mockRest.addHandler('fetch', ({baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp`); + return response; + }); + + SoftwareProductComponentsMonitoringActionHelper.fetchExistingFiles(store.dispatch, { + softwareProductId, + componentId + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual(response.snmpPoll); + expect(monitoring.trapFilename).toEqual(response.snmpTrap); + done(); + }, 0); + }); + + it('Upload snmp trap file', done => { + + mockRest.addHandler('create', ({baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp-trap/upload`); + return {}; + }); + var debug = {hello: 'world'}; + let file = new Blob([JSON.stringify(debug, null, 2)], {type: 'application/json'});; + let formData = new FormData(); + formData.append('upload', file); + SoftwareProductComponentsMonitoringActionHelper.uploadSnmpFile(store.dispatch, { + softwareProductId, + componentId, + formData, + fileSize: file.size, + type: SoftwareProductComponentsMonitoringConstants.SNMP_TRAP + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual(undefined); + expect(monitoring.trapFilename).toEqual('blob'); + done(); + }, 0); + }); + + it('Upload snmp poll file', done => { + mockRest.addHandler('create', ({baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp/upload`); + return {}; + }); + var debug = {hello: 'world'}; + let file = new Blob([JSON.stringify(debug, null, 2)], {type: 'application/json'});; + let formData = new FormData(); + formData.append('upload', file); + SoftwareProductComponentsMonitoringActionHelper.uploadSnmpFile(store.dispatch, { + softwareProductId, + componentId, + formData, + fileSize: file.size, + type: SoftwareProductComponentsMonitoringConstants.SNMP_POLL + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual('blob'); + expect(monitoring.trapFilename).toEqual(undefined); + done(); + }, 0); + }); + + it('Delete snmp trap file', done => { + mockRest.addHandler('destroy', ({baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp-trap`); + return {}; + }); + SoftwareProductComponentsMonitoringActionHelper.deleteSnmpFile(store.dispatch, { + softwareProductId, + componentId, + type: SoftwareProductComponentsMonitoringConstants.SNMP_TRAP + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.trapFilename).toEqual(undefined); + done(); + }, 0); + }); + + it('Delete snmp poll file', done => { + mockRest.addHandler('destroy', ({baseUrl}) => { + expect(baseUrl).toEqual(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/monitors/snmp`); + return {}; + }); + SoftwareProductComponentsMonitoringActionHelper.deleteSnmpFile(store.dispatch, { + softwareProductId, + componentId, + type: SoftwareProductComponentsMonitoringConstants.SNMP_POLL + }); + setTimeout(()=> { + var {softwareProduct: {softwareProductComponents: {monitoring}}} = store.getState(); + expect(monitoring.pollFilename).toEqual(undefined); + done(); + }, 0); + }); +}); diff --git a/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNICEditor.test.js b/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNICEditor.test.js new file mode 100644 index 0000000000..c9760f7799 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNICEditor.test.js @@ -0,0 +1,97 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js'; +import SoftwareProductComponentsNICEditorView from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx'; + + + +describe('Software Product Component Network NIC Editor and View Classes', () => { + it('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + + it('mapStateToProps data test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + lockingUser: 'cs0008' + }; + + + var obj = { + softwareProduct: { + softwareProductEditor: { + data: currentSoftwareProduct + }, + softwareProductComponents: { + network: { + nicEditor: { + data: {}, + qdata: {}, + qschema: {} + } + } + } + } + }; + + var results = mapStateToProps(obj); + expect(results.currentSoftwareProduct).toExist(); + expect(results.qdata).toExist(); + expect(results.qschema).toExist(); + expect(results.data).toExist(); + }); + + + it('Software Product Component Network NIC Editor View Test', () => { + + const data = { + name: '', + description: '', + networkName: '' + }; + + const qdata = {}; + const qschema = {}; + + var renderer = TestUtils.createRenderer(); + renderer.render( + ); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + + }); +}); diff --git a/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNetwork.test.js b/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNetwork.test.js new file mode 100644 index 0000000000..520fde7403 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNetwork.test.js @@ -0,0 +1,125 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js'; +import SoftwareProductComponentsNetworkListView from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + + +describe('Software Product Component Network Mapper and View Classes', () => { + + it('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + it('mapStateToProps data test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + + var obj = { + softwareProduct: { + softwareProductEditor: { + data: currentSoftwareProduct + }, + softwareProductComponents: { + componentEditor: { + qdata: {}, + qschema: {}, + data: {} + }, + network: { + nicEditor: {}, + nicList: [] + } + } + } + }; + + var results = mapStateToProps(obj); + expect(results.qdata).toExist(); + expect(results.qschema).toExist(); + expect(results.componentData).toExist(); + }); + + it('Software Product Component Network List View Test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_IN_STATUS, + lockingUser: 'cs0008' + }; + + const versionControllerData = { + version: '1', + viewableVersions: [], + status: 'locked', + isCheckedOut: true + }; + + const nicList = [ + { + name: 'name', + networkId: 'network', + id: '122', + networkName: 'nname' + } + ]; + + var renderer = TestUtils.createRenderer(); + renderer.render( + ); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + + + + + }); + +}); diff --git a/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.test.js b/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.test.js new file mode 100644 index 0000000000..8c23267c89 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.test.js @@ -0,0 +1,305 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import SoftwareProductComponentsNetworkActionHelper from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js'; + +const softwareProductId = '123'; +const componentId = '321'; +const nicId = '111'; + +describe('Software Product Components Network Action Helper Tests', function () { + + it('Fetch NICs List', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const NICList = [ + { + name:'oam01_port_0', + description:'bbbbbbb', + networkId:'A0E578751B284D518ED764D5378EA97C', + id:'96D3648338F94DAA9889E9FBB8E59895', + networkName:'csb_net' + }, + { + name:'oam01_port_1', + description:'bbbbbbb', + networkId:'378EA97CA0E578751B284D518ED764D5', + id:'8E5989596D3648338F94DAA9889E9FBB', + networkName:'csb_net_2' + } + + ]; + + deepFreeze(NICList); + + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network.nicList', NICList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return {results: NICList}; + }); + + return SoftwareProductComponentsNetworkActionHelper.fetchNICsList(store.dispatch, {softwareProductId, componentId}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + + }); + + it('open NICE editor', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + const data = { + name: 'oam01_port_0', + description: 'bbbbbbb', + networkId: 'A0E578751B284D518ED764D5378EA97C', + networkName: 'csb_net' + }; + + const nic = {id: '444'}; + deepFreeze(data); + deepFreeze(nic); + + const expectedData = {...data, id: nic.id}; + + deepFreeze(expectedData); + + const network = { + nicEditor: { + data: expectedData + }, + nicList: [] + }; + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network', network); + + SoftwareProductComponentsNetworkActionHelper.openNICEditor(store.dispatch, {nic, data}); + + return setTimeout(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }, 100); + }); + + it('close NICE editor', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const network = { + nicEditor: {}, + nicList: [] + }; + deepFreeze(network); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network', network); + + SoftwareProductComponentsNetworkActionHelper.closeNICEditor(store.dispatch); + + return setTimeout(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }, 100); + }); + + it('Load NIC data', () => { + + const expectedData = { + description: 'bbbbbbb', + name: 'oam01_port_0', + networkId: 'A0E578751B284D518ED764D5378EA97C', + networkName: 'csb_net' + }; + + deepFreeze(expectedData); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics/${nicId}`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return (expectedData); + }); + + return SoftwareProductComponentsNetworkActionHelper.loadNICData({softwareProductId, componentId, nicId}).then((data) => { + expect(data).to.deep.equal(expectedData); + }); + }); + + + it('load NIC Questionnaire', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const qdata = { + protocols: { + protocolWithHighestTrafficProfile: 'UDP', + protocols: ['UDP'] + }, + ipConfiguration: { + ipv4Required: true + } + }; + + const qschema = { + $schema: 'http://json-schema.org/draft-04/schema#', + type: 'object', + properties: { + 'protocols': { + type: 'object', + properties: {} + } + } + }; + + deepFreeze(qdata); + deepFreeze(qschema); + + + const network = { + nicEditor: { + qdata, + qschema + }, + nicList: [] + }; + deepFreeze(network); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network', network); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics/${nicId}/questionnaire`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return ({data: JSON.stringify(qdata), schema: JSON.stringify(qschema)}); + }); + + return SoftwareProductComponentsNetworkActionHelper.loadNICQuestionnaire(store.dispatch, {softwareProductId, componentId, nicId}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('update NIC Data', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const data = {test: '123'}; + deepFreeze(data); + + const network = { + nicEditor: { + data + }, + nicList: [] + }; + + deepFreeze(network); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network', network); + + SoftwareProductComponentsNetworkActionHelper.updateNICData(store.dispatch, {deltaData:data}); + + return setTimeout(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }, 100); + + }); + + it('update NIC Questionnaire', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const qdata = { + test: '123' + }; + const network = { + nicEditor: { + qdata, + qschema: undefined + }, + nicList: [] + }; + deepFreeze(network); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network', network); + + SoftwareProductComponentsNetworkActionHelper.updateNICQuestionnaire(store.dispatch, {data:qdata}); + + return setTimeout(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }, 100); + + }); + + it('save NIC Data And Questionnaire', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const qdata = { + qtest: '111' + }; + const data = { + name: '2222', + description: 'blabla', + networkId: '123445' + }; + + const expectedData = {...data, id: nicId}; + + const network = { + nicEditor: {}, + nicList: [ + expectedData + ] + }; + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.network', network); + deepFreeze(expectedStore); + + mockRest.addHandler('save', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics/${nicId}/questionnaire`); + expect(data).to.deep.equal(qdata); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + mockRest.addHandler('save', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics/${nicId}`); + expect(data).to.deep.equal(data); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(store.dispatch, {softwareProductId, componentId, qdata, data: expectedData}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + +}); diff --git a/openecomp-ui/test/softwareProduct/components/processes/test.js b/openecomp-ui/test/softwareProduct/components/processes/test.js new file mode 100644 index 0000000000..67427d3c05 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/processes/test.js @@ -0,0 +1,214 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import SoftwareProductComponentProcessesActionHelper from 'sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js'; + +const softwareProductId = '123'; +const componentId = '222'; +describe('Software Product Component Processes Module Tests', function () { + it('Get Software Products Processes List', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductProcessesList = [ + { + name: 'Pr1', + description: 'hjhj', + id: 'EBADF561B7FA4A788075E1840D0B5971', + artifactName: 'artifact' + }, + { + name: 'Pr1', + description: 'hjhj', + id: '2F47447D22DB4C53B020CA1E66201EF2', + artifactName: 'artifact' + } + ]; + + deepFreeze(softwareProductProcessesList); + + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentProcesses.processesList', softwareProductProcessesList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return {results: softwareProductProcessesList}; + }); + + return SoftwareProductComponentProcessesActionHelper.fetchProcessesList(store.dispatch, {softwareProductId, componentId}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + it('Delete Software Products Processes', () => { + const softwareProductProcessesList = [ + { + name: 'Pr1', + description: 'hjhj', + id: 'EBADF561B7FA4A788075E1840D0B5971', + artifactName: 'artifact' + } + ]; + + deepFreeze(softwareProductProcessesList); + const store = storeCreator({ + softwareProduct: { + softwareProductProcesses: { + processesList: softwareProductProcessesList + } + } + }); + + const processId = 'EBADF561B7FA4A788075E1840D0B5971'; + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentProcesses.processesList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes/${processId}`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return { + results: { + returnCode: 'OK' + } + }; + }); + + return SoftwareProductComponentProcessesActionHelper.deleteProcess(store.dispatch, { + process: softwareProductProcessesList[0], + softwareProductId, componentId + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add Software Products Processes', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductPostRequest = { + name: 'Pr1', + description: 'string' + }; + const softwareProductProcessToAdd = { + name: 'Pr1', + description: 'string' + }; + const softwareProductProcessFromResponse = 'ADDED_ID'; + const softwareProductProcessAfterAdd = { + ...softwareProductProcessToAdd, + id: softwareProductProcessFromResponse + }; + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentProcesses.processesList', [softwareProductProcessAfterAdd]); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`); + expect(data).to.deep.equal(softwareProductPostRequest); + expect(options).to.equal(undefined); + return { + returnCode: 'OK', + value: softwareProductProcessFromResponse + }; + }); + + return SoftwareProductComponentProcessesActionHelper.saveProcess(store.dispatch, + { + softwareProductId, + previousProcess: null, + process: softwareProductProcessToAdd, + componentId + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update Software Products Processes', () => { + const softwareProductProcessesList = [ + { + name: 'Pr1', + description: 'string', + id: 'EBADF561B7FA4A788075E1840D0B5971', + artifactName: 'artifact' + } + ]; + deepFreeze(softwareProductProcessesList); + + const store = storeCreator({ + softwareProduct: { + softwareProductComponents: { + componentProcesses: { + processesList: softwareProductProcessesList + } + } + } + }); + deepFreeze(store.getState()); + + const toBeUpdatedProcessId = softwareProductProcessesList[0].id; + const previousProcessData = softwareProductProcessesList[0]; + const processUpdateData = { + ...softwareProductProcessesList[0], + name: 'Pr1_UPDATED', + description: 'string_UPDATED' + }; + deepFreeze(processUpdateData); + + const processPutRequest = { + name: 'Pr1_UPDATED', + description: 'string_UPDATED' + }; + deepFreeze(processPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentProcesses.processesList', [processUpdateData]); + + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes/${toBeUpdatedProcessId}`); + expect(data).to.deep.equal(processPutRequest); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductComponentProcessesActionHelper.saveProcess(store.dispatch, + { + softwareProductId: softwareProductId, + componentId, + previousProcess: previousProcessData, + process: processUpdateData + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + +}); + diff --git a/openecomp-ui/test/softwareProduct/components/storage/test.js b/openecomp-ui/test/softwareProduct/components/storage/test.js new file mode 100644 index 0000000000..87cad368be --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/storage/test.js @@ -0,0 +1,132 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; + +const softwareProductId = '123'; +const vspComponentId = '111'; + +describe('Software Product Components Storage Module Tests', function () { + + let restPrefix = ''; + + before(function() { + restPrefix = Configuration.get('restPrefix'); + deepFreeze(restPrefix); + }); + + it('Get Software Products Components Storage', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductComponentStorage = { + data: JSON.stringify({'backup':{'backupType':'OnSite','backupSolution':'76333'},'snapshotBackup':{'snapshotFrequency':'2'}}), + schema: JSON.stringify({'backup':{'backupType':'OnSite','backupSolution':'76333'},'snapshotBackup':{'snapshotFrequency':'2'}}) + }; + deepFreeze(softwareProductComponentStorage); + + const softwareProductComponentStorageData = { + qdata: JSON.parse(softwareProductComponentStorage.data), + qschema: JSON.parse(softwareProductComponentStorage.schema) + }; + deepFreeze(softwareProductComponentStorageData); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor', softwareProductComponentStorageData); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).toEqual(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${vspComponentId}/questionnaire`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return softwareProductComponentStorage; + }); + + return SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(store.dispatch, {softwareProductId, vspComponentId}).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Get Empty Software Products Components Storage', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductComponentQuestionnaire = { + data: null, + schema: JSON.stringify({'backup':{'backupType':'OnSite','backupSolution':'76333'},'snapshotBackup':{'snapshotFrequency':'2'}}) + }; + deepFreeze(softwareProductComponentQuestionnaire); + + const softwareProductComponentQuestionnaireData = { + qdata: {}, + qschema: JSON.parse(softwareProductComponentQuestionnaire.schema) + }; + deepFreeze(softwareProductComponentQuestionnaireData); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor', softwareProductComponentQuestionnaireData); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).toEqual(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${vspComponentId}/questionnaire`); + expect(data).toEqual(undefined); + expect(options).toEqual(undefined); + return softwareProductComponentQuestionnaire; + }); + + return SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(store.dispatch, {softwareProductId, vspComponentId}).then(() => { + expect(store.getState()).toEqual(expectedStore); + }); + }); + + it('Update Software Products Components Storage', () => { + const store = storeCreator({ + softwareProduct: { + softwareProductComponents: { + componentEditor: { + qdata: { + backupType: 'OnSite', + backupStorageSize: 30 + }, + qschema: { + type: 'object', + properties: { + backupType: {type: 'string'}, + backupStorageSize: {type: 'number'} + } + } + } + } + } + }); + deepFreeze(store); + + const data = {backupType: 'OffSite', backupStorageSize: 30}; + deepFreeze(data); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor.qdata', data); + + SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(store.dispatch, {data}); + + expect(store.getState()).toEqual(expectedStore); + }); +}); diff --git a/openecomp-ui/test/softwareProduct/components/test.js b/openecomp-ui/test/softwareProduct/components/test.js new file mode 100644 index 0000000000..839e1b7cf7 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/components/test.js @@ -0,0 +1,101 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js'; + +const softwareProductId = '123'; +const vspComponentId = '321'; + +describe('Software Product Components Module Tests', function () { + it('Get Software Products Components List', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductComponentsList = [ + { + name: 'com.d2.resource.vfc.nodes.heat.sm_server', + displayName: 'sm_server', + description: 'hjhj', + id: 'EBADF561B7FA4A788075E1840D0B5971' + }, + { + name: 'com.d2.resource.vfc.nodes.heat.pd_server', + displayName: 'pd_server', + description: 'hjhj', + id: '2F47447D22DB4C53B020CA1E66201EF2' + } + ]; + + deepFreeze(softwareProductComponentsList); + + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentsList', softwareProductComponentsList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return {results: softwareProductComponentsList}; + }); + + return SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(store.dispatch, {softwareProductId}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update SoftwareProduct Component Questionnaire', () => { + const store = storeCreator(); + + const qdataUpdated = { + general: { + hypervisor: { + containerFeatureDescription: 'aaaUpdated', + drivers: 'bbbUpdated', + hypervisor: 'cccUpdated' + } + } + }; + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductComponents.componentEditor.qdata', qdataUpdated); + deepFreeze(expectedStore); + + + mockRest.addHandler('save', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/components/${vspComponentId}/questionnaire`); + expect(data).to.deep.equal(qdataUpdated); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(store.dispatch, {softwareProductId, vspComponentId, qdata: qdataUpdated}).then(() => { + //TODO think should we add here something or not + }); + + + }); + +}); + diff --git a/openecomp-ui/test/softwareProduct/details/detailsView.test.js b/openecomp-ui/test/softwareProduct/details/detailsView.test.js new file mode 100644 index 0000000000..b6a8ca5d4e --- /dev/null +++ b/openecomp-ui/test/softwareProduct/details/detailsView.test.js @@ -0,0 +1,438 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js'; +import SoftwareProductDetailsView from 'sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx'; +import {vspQschema as vspQuestionnaireSchema} from './vspQschema.js'; + +describe('Software Product Details: ', function () { + + let currentSoftwareProduct = {}, categories = [], finalizedLicenseModelList, licenseAgreementList, featureGroupsList, vspQschema; + let dummyFunc = () => {}; + + before(function() { + currentSoftwareProduct = { + id: 'D4774719D085414E9D5642D1ACD59D20', + name: 'VSP', + description: 'dfdf', + category: 'category1', + subCategory: 'category1.subCategory', + vendorId: 'VLM_ID1', + vendorName: 'VLM1', + licensingVersion: '1.0', + licensingData: {} + }; + categories = [{ + uniqueId: 'category1', + subcategories: [{ + uniqueId: 'subCategory' + }] + }, { + uniqueId: 'category2', + subcategories: [{ + uniqueId: 'subCategory2' + }] + }]; + finalizedLicenseModelList = [{ + id: 'VLM_ID1', + name: 'VLM1' + }]; + licenseAgreementList = [{id: 'LA_ID1'}, {id: 'LA_ID2'}]; + featureGroupsList = [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ]; + vspQschema = vspQuestionnaireSchema; + }); + + it('should mapper exist', () => { + expect(mapStateToProps).toExist(); + }); + + it('should mapper return vsp basic data', () => { + var obj = { + softwareProduct: { + softwareProductEditor: { + data: currentSoftwareProduct + }, + softwareProductCategories: categories, + softwareProductQuestionnaire: { + qdata: {}, + qschema: vspQschema + } + }, + finalizedLicenseModelList: finalizedLicenseModelList, + licenseModel: { + licenseAgreement: { + licenseAgreementList: [] + }, + featureGroup: { + featureGroupsList: [] + } + } + }; + + var result = mapStateToProps(obj); + expect(result.currentSoftwareProduct).toEqual(currentSoftwareProduct); + expect(result.finalizedLicenseModelList).toEqual(finalizedLicenseModelList); + expect(result.finalizedLicenseModelList.length).toBeGreaterThan(0); + expect(finalizedLicenseModelList).toInclude({ + id: result.currentSoftwareProduct.vendorId, + name: result.currentSoftwareProduct.vendorName + }); + expect(result.softwareProductCategories).toEqual(categories); + expect(result.licenseAgreementList).toEqual([]); + expect(result.featureGroupsList).toEqual([]); + expect(result.qdata).toEqual({}); + expect(result.qschema).toEqual(vspQschema); + expect(result.isReadOnlyMode).toEqual(true); + }); + + it('should mapper return vsp data with selected licenseAgreement and featureGroup', () => { + let vspWithLicensingData = { + ...currentSoftwareProduct, + licensingData: { + licenseAgreement: 'LA_ID1', + featureGroups: [{enum: 'FG_ID1', title: 'FG1'}] + } + }; + var obj = { + softwareProduct: { + softwareProductEditor: { + data: vspWithLicensingData + }, + softwareProductCategories: categories, + softwareProductQuestionnaire: { + qdata: {}, + qschema: vspQschema + } + }, + finalizedLicenseModelList: finalizedLicenseModelList, + licenseModel: { + licenseAgreement: { + licenseAgreementList: licenseAgreementList + }, + featureGroup: { + featureGroupsList: featureGroupsList + } + } + }; + + var result = mapStateToProps(obj); + expect(result.currentSoftwareProduct).toEqual(vspWithLicensingData); + expect(result.finalizedLicenseModelList).toEqual(finalizedLicenseModelList); + expect(result.finalizedLicenseModelList.length).toBeGreaterThan(0); + expect(result.finalizedLicenseModelList).toInclude({ + id: result.currentSoftwareProduct.vendorId, + name: result.currentSoftwareProduct.vendorName + }); + expect(result.softwareProductCategories).toEqual(categories); + expect(result.licenseAgreementList).toEqual(licenseAgreementList); + expect(result.licenseAgreementList).toInclude({id: result.currentSoftwareProduct.licensingData.licenseAgreement}); + result.currentSoftwareProduct.licensingData.featureGroups.forEach(fg => { + expect(featureGroupsList).toInclude({ + id: fg.enum, + name: fg.title, + referencingLicenseAgreements: [result.currentSoftwareProduct.licensingData.licenseAgreement] + }); + expect(result.featureGroupsList).toInclude(fg); + }); + expect(result.qdata).toEqual({}); + expect(result.qschema).toEqual(vspQschema); + expect(result.isReadOnlyMode).toEqual(true); + }); + + it('VSP Details view test', () => { + let params = { + currentSoftwareProduct: currentSoftwareProduct, + softwareProductCategories: categories, + qdata: {}, + qschema: vspQschema, + finalizedLicenseModelList: [{ + id: 'VLM_ID1', + vendorName: 'VLM1', + version: '2.0', + viewableVersions: ['1.0', '2.0'] + }, { + id: 'VLM_ID2', + vendorName: 'VLM2', + version: '3.0', + viewableVersions: ['1.0', '2.0', '3.0'] + }], + licenseAgreementList: [{id: 'LA_ID1'}, {id: 'LA_ID2'}], + featureGroupsList: [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ] + }; + var renderer = TestUtils.createRenderer(); + renderer.render( + + ); + let renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + }); + + it('in view: should change vendorId and update vsp licensing-version', done => { + let vspWithLicensingData = { + ...currentSoftwareProduct, + licensingData: { + licenseAgreement: 'LA_ID1', + featureGroups: [{enum: 'FG_ID1', title: 'FG1'}] + } + }; + let params = { + currentSoftwareProduct: vspWithLicensingData, + softwareProductCategories: categories, + qdata: {}, + qschema: vspQschema, + finalizedLicenseModelList: [{ + id: 'VLM_ID1', + vendorName: 'VLM1', + version: '2.0', + viewableVersions: ['1.0', '2.0'] + }, { + id: 'VLM_ID2', + vendorName: 'VLM2', + version: '3.0', + viewableVersions: ['1.0', '2.0', '3.0'] + }], + licenseAgreementList: [{id: 'LA_ID1'}, {id: 'LA_ID2'}], + featureGroupsList: [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ] + }; + const onVendorChangedListener = (deltaData) => { + expect(deltaData.vendorId).toEqual('VLM_ID2'); + expect(deltaData.vendorName).toEqual('VLM2'); + expect(deltaData.licensingVersion).toEqual(''); + expect(deltaData.licensingData).toEqual({}); + done(); + }; + + var vspDetailsView = TestUtils.renderIntoDocument( onVendorChangedListener(deltaData)}/>); + expect(vspDetailsView).toExist(); + vspDetailsView.onVendorParamChanged({vendorId: 'VLM_ID2'}); + }); + + it('in view: should change licensing-version and update licensing data', done => { + let params = { + currentSoftwareProduct: currentSoftwareProduct, + softwareProductCategories: categories, + qdata: {}, + qschema: vspQschema, + finalizedLicenseModelList: [{ + id: 'VLM_ID1', + vendorName: 'VLM1', + version: '2.0', + viewableVersions: ['1.0', '2.0'] + }, { + id: 'VLM_ID2', + vendorName: 'VLM2', + version: '3.0', + viewableVersions: ['1.0', '2.0', '3.0'] + }], + licenseAgreementList: [{id: 'LA_ID1'}, {id: 'LA_ID2'}], + featureGroupsList: [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ] + }; + const onVendorChangedListener = (deltaData) => { + expect(deltaData.vendorId).toEqual('VLM_ID2'); + expect(deltaData.vendorName).toEqual('VLM2'); + expect(deltaData.licensingVersion).toEqual('2.0'); + expect(deltaData.licensingData).toEqual({}); + done(); + }; + + let vspDetailsView = TestUtils.renderIntoDocument( onVendorChangedListener(deltaData)}/>); + expect(vspDetailsView).toExist(); + vspDetailsView.onVendorParamChanged({vendorId: 'VLM_ID2', licensingVersion: '2.0'}); + }); + + it('in view: should change subcategory', done => { + let params = { + currentSoftwareProduct: currentSoftwareProduct, + softwareProductCategories: categories, + qdata: {}, + qschema: vspQschema, + finalizedLicenseModelList: [{ + id: 'VLM_ID1', + vendorName: 'VLM1', + version: '2.0', + viewableVersions: ['1.0', '2.0'] + }, { + id: 'VLM_ID2', + vendorName: 'VLM2', + version: '3.0', + viewableVersions: ['1.0', '2.0', '3.0'] + }], + licenseAgreementList: [{id: 'LA_ID1'}, {id: 'LA_ID2'}], + featureGroupsList: [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ] + }; + const onDataChangedListener = ({category, subCategory}) => { + expect(category).toEqual('category2'); + expect(subCategory).toEqual('subCategory2'); + done(); + }; + + let vspDetailsView = TestUtils.renderIntoDocument( onDataChangedListener({category, subCategory})} + onValidityChanged = {dummyFunc} + onQDataChanged = {dummyFunc} + onVendorParamChanged = {dummyFunc}/>); + expect(vspDetailsView).toExist(); + vspDetailsView.onSelectSubCategory('subCategory2'); + }); + + it('in view: should change feature groups', done => { + let vspWithLicensingData = { + ...currentSoftwareProduct, + licensingData: { + licenseAgreement: 'LA_ID1', + featureGroups: [{enum: 'FG_ID1', title: 'FG1'}] + } + }; + let params = { + currentSoftwareProduct: vspWithLicensingData, + softwareProductCategories: categories, + qdata: {}, + qschema: vspQschema, + finalizedLicenseModelList: [{ + id: 'VLM_ID1', + vendorName: 'VLM1', + version: '2.0', + viewableVersions: ['1.0', '2.0'] + }, { + id: 'VLM_ID2', + vendorName: 'VLM2', + version: '3.0', + viewableVersions: ['1.0', '2.0', '3.0'] + }], + licenseAgreementList: [{id: 'LA_ID1'}, {id: 'LA_ID2'}], + featureGroupsList: [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ] + }; + const onDataChangedListener = ({licensingData}) => { + expect(licensingData.licenseAgreement).toEqual('LA_ID1'); + expect(licensingData.featureGroups).toEqual([ + {enum: 'FG_ID1', title: 'FG1'}, + {enum: 'FG_ID2', title: 'FG2'} + ]); + done(); + }; + + let vspDetailsView = TestUtils.renderIntoDocument( onDataChangedListener({licensingData})} + onValidityChanged = {dummyFunc} + onQDataChanged = {dummyFunc} + onVendorParamChanged = {dummyFunc}/>); + expect(vspDetailsView).toExist(); + vspDetailsView.onFeatureGroupsChanged({featureGroups: [ + {enum: 'FG_ID1', title: 'FG1'}, + {enum: 'FG_ID2', title: 'FG2'} + ]}); + }); + + it('in view: should change license agreement', done => { + let vspWithLicensingData = { + ...currentSoftwareProduct, + licensingData: { + licenseAgreement: 'LA_ID1', + featureGroups: [{enum: 'FG_ID1', title: 'FG1'}] + } + }; + let params = { + currentSoftwareProduct: vspWithLicensingData, + softwareProductCategories: categories, + qdata: {}, + qschema: vspQschema, + finalizedLicenseModelList: [{ + id: 'VLM_ID1', + vendorName: 'VLM1', + version: '2.0', + viewableVersions: ['1.0', '2.0'] + }, { + id: 'VLM_ID2', + vendorName: 'VLM2', + version: '3.0', + viewableVersions: ['1.0', '2.0', '3.0'] + }], + licenseAgreementList: [{id: 'LA_ID1'}, {id: 'LA_ID2'}], + featureGroupsList: [ + {id: 'FG_ID1', name: 'FG1', referencingLicenseAgreements: ['LA_ID1']}, + {id: 'FG_ID2', name: 'FG2', referencingLicenseAgreements: ['LA_ID1']} + ] + }; + const onDataChangedListener = ({licensingData}) => { + expect(licensingData.licenseAgreement).toEqual('LA_ID2'); + expect(licensingData.featureGroups).toEqual([]); + done(); + }; + + let vspDetailsView = TestUtils.renderIntoDocument( onDataChangedListener({licensingData})} + onValidityChanged = {dummyFunc} + onQDataChanged = {dummyFunc} + onVendorParamChanged = {dummyFunc}/>); + expect(vspDetailsView).toExist(); + vspDetailsView.onLicensingDataChanged({licenseAgreement: 'LA_ID2', featureGroups: []}); + }); +}); diff --git a/openecomp-ui/test/softwareProduct/details/test.js b/openecomp-ui/test/softwareProduct/details/test.js new file mode 100644 index 0000000000..9803b1611d --- /dev/null +++ b/openecomp-ui/test/softwareProduct/details/test.js @@ -0,0 +1,383 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; + +import SoftwareProductCreationActionHelper from 'sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js'; +import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js'; +import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js'; + +describe('Software Product Module Tests', function () { + it('Get Software Products List', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductList = [ + { + name: 'VSP1', + description: 'hjhj', + version: '0.1', + id: 'EBADF561B7FA4A788075E1840D0B5971', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '5259EDE4CC814DC9897BA6F69E2C971B', + vendorName: 'Vendor', + checkinStatus: 'CHECK_OUT', + licensingData: { + 'featureGroups': [] + } + }, + { + name: 'VSP2', + description: 'dfdfdfd', + version: '0.1', + id: '2F47447D22DB4C53B020CA1E66201EF2', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '5259EDE4CC814DC9897BA6F69E2C971B', + vendorName: 'Vendor', + checkinStatus: 'CHECK_OUT', + licensingData: { + featureGroups: [] + } + } + ]; + + deepFreeze(softwareProductList); + + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProductList', softwareProductList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal('/onboarding-api/v1.0/vendor-software-products/'); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return {results: softwareProductList}; + }); + + return SoftwareProductActionHelper.fetchSoftwareProductList(store.dispatch).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add Software Product', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductPostRequest = deepFreeze({ + name: 'vsp1', + description: 'string', + vendorId: '1', + vendorName: 'Vendor', + icon: 'icon', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + licensingData: {} + }); + + const softwareProductToAdd = deepFreeze({ + ...softwareProductPostRequest + }); + + const softwareProductIdFromResponse = 'ADDED_ID'; + const softwareProductAfterAdd = deepFreeze({ + ...softwareProductToAdd, + id: softwareProductIdFromResponse + }); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProductList', [softwareProductAfterAdd]); + + mockRest.addHandler('create', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal('/onboarding-api/v1.0/vendor-software-products/'); + expect(data).to.deep.equal(softwareProductPostRequest); + expect(options).to.equal(undefined); + return { + vspId: softwareProductIdFromResponse + }; + }); + + return SoftwareProductCreationActionHelper.createSoftwareProduct(store.dispatch, { + softwareProduct: softwareProductToAdd + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + it('Save Software product', () => { + const softwareProduct = { + name: 'VSP5', + id: '4730033D16C64E3CA556AB0AC4478218', + description: 'A software model for Fortigate.', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '1', + vendorName: 'Vendor', + licensingVersion: '1.0', + icon: 'icon', + licensingData: { + licenceAgreement: '123', + featureGroups: [ + '123', '234' + ] + } + }; + deepFreeze(softwareProduct); + + const store = storeCreator({ + softwareProduct: { + softwareProductEditor: {data: softwareProduct}, + softwareProductQuestionnaire: {qdata: 'test', qschema: {type: 'string'}} + } + }); + deepFreeze(store.getState()); + + const toBeUpdatedSoftwareProductId = softwareProduct.id; + const softwareProductUpdateData = { + ...softwareProduct, + name: 'VSP5_UPDATED', + description: 'A software model for Fortigate._UPDATED' + }; + deepFreeze(softwareProductUpdateData); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProductList', [softwareProductUpdateData]); + const questionnaireData = { + general: { + affinityData: { + affinityGrouping: true, + antiAffinityGrouping: false + } + } + }; + deepFreeze(questionnaireData); + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + const expectedData = { + name: 'VSP5_UPDATED', + description: 'A software model for Fortigate._UPDATED', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '1', + vendorName: 'Vendor', + licensingVersion: '1.0', + icon: 'icon', + licensingData: { + licenceAgreement: '123', + featureGroups: [ + '123', '234' + ] + } + }; + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${toBeUpdatedSoftwareProductId}`); + expect(data).to.deep.equal(expectedData); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${toBeUpdatedSoftwareProductId}/questionnaire`); + expect(data).to.deep.equal(questionnaireData); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductActionHelper.updateSoftwareProduct(store.dispatch, { + softwareProduct: softwareProductUpdateData, + qdata: questionnaireData + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + it('Save Software product data only', () => { + const softwareProduct = { + name: 'VSP5', + id: '4730033D16C64E3CA556AB0AC4478218', + description: 'A software model for Fortigate.', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '1', + vendorName: 'Vendor', + licensingVersion: '1.0', + icon: 'icon', + licensingData: { + licenceAgreement: '123', + featureGroups: [ + '123', '234' + ] + } + }; + deepFreeze(softwareProduct); + + const store = storeCreator({ + softwareProduct: { + softwareProductEditor: {data: softwareProduct}, + softwareProductQuestionnaire: {qdata: 'test', qschema: {type: 'string'}} + } + }); + deepFreeze(store.getState()); + const expectedStore = store.getState(); + + const toBeUpdatedSoftwareProductId = softwareProduct.id; + const softwareProductUpdateData = { + ...softwareProduct, + name: 'VSP5_UPDATED', + description: 'A software model for Fortigate._UPDATED' + }; + deepFreeze(softwareProductUpdateData); + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + const expectedData = { + name: 'VSP5_UPDATED', + description: 'A software model for Fortigate._UPDATED', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '1', + vendorName: 'Vendor', + licensingVersion: '1.0', + icon: 'icon', + licensingData: { + licenceAgreement: '123', + featureGroups: [ + '123', '234' + ] + } + }; + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${toBeUpdatedSoftwareProductId}`); + expect(data).to.deep.equal(expectedData); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductActionHelper.updateSoftwareProductData(store.dispatch, { + softwareProduct: softwareProductUpdateData + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Save Software product questionnaire only', () => { + const softwareProduct = { + name: 'VSP5', + id: '4730033D16C64E3CA556AB0AC4478218', + description: 'A software model for Fortigate.', + subCategory: 'resourceNewCategory.network connectivity.virtual links', + category: 'resourceNewCategory.network connectivity', + vendorId: '1', + vendorName: 'Vendor', + icon: 'icon', + licensingData: { + licenceAgreement: '123', + featureGroups: [ + '123', '234' + ] + } + }; + deepFreeze(softwareProduct); + + const store = storeCreator({ + softwareProduct: { + softwareProductEditor: {data: softwareProduct}, + softwareProductQuestionnaire: {qdata: 'test', qschema: {type: 'string'}} + } + }); + deepFreeze(store.getState()); + const expectedStore = store.getState(); + + const toBeUpdatedSoftwareProductId = softwareProduct.id; + const questionnaireData = { + general: { + affinityData: { + affinityGrouping: true, + antiAffinityGrouping: false + } + } + }; + deepFreeze(questionnaireData); + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${toBeUpdatedSoftwareProductId}/questionnaire`); + expect(data).to.deep.equal(questionnaireData); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(store.dispatch, { + softwareProductId: softwareProduct.id, + qdata: questionnaireData + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Handle category without subcategories', () => { + const categories = deepFreeze([ + { + name: 'Resource Category 1', + normalizedName: 'resource category 1', + uniqueId: 'resourceNewCategory.resource category 1', + subcategories: [ + { + name: 'Sub Category for RC 1', + normalizedName: 'sub category for rc 1', + uniqueId: 'resourceNewCategory.resource category 1.sub category for rc 1' + }, + { + name: 'SC4RC2', + normalizedName: 'sc4rc2', + uniqueId: 'resourceNewCategory.resource category 1.sc4rc2' + }, + { + name: 'SC4RC1', + normalizedName: 'sc4rc1', + uniqueId: 'resourceNewCategory.resource category 1.sc4rc1' + } + ] + }, + { + name: 'Eeeeee', + normalizedName: 'eeeeee', + uniqueId: 'resourceNewCategory.eeeeee' + }, + { + name: 'Some Recource', + normalizedName: 'some recource', + uniqueId: 'resourceNewCategory.some recource', + subcategories: [ + { + name: 'Second Sub Category for S', + normalizedName: 'second sub category for s', + uniqueId: 'resourceNewCategory.some recource.second sub category for s' + }, + { + name: 'Sub Category for Some Rec', + normalizedName: 'sub category for some rec', + uniqueId: 'resourceNewCategory.some recource.sub category for some rec' + } + ] + } + ]); + const category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory('resourceNewCategory.some recource.sub category for some rec', categories); + expect(category).to.equal('resourceNewCategory.some recource'); + }); + +}); + diff --git a/openecomp-ui/test/softwareProduct/details/vspQschema.js b/openecomp-ui/test/softwareProduct/details/vspQschema.js new file mode 100644 index 0000000000..5612b19991 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/details/vspQschema.js @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +export const vspQschema = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'type': 'object', + 'properties': { + 'general': { + 'type': 'object', + 'properties': { + 'affinityData': { + 'type': 'object', + 'properties': { + 'affinityGrouping': {}, + 'antiAffinityGrouping': {} + } + }, + 'availability': { + 'type': 'object', + 'properties': { + 'useAvailabilityZonesForHighAvailability': {} + } + }, + 'regionsData': { + 'type': 'object', + 'properties': { + 'multiRegion': {}, + 'regions': {} + } + }, + 'storageDataReplication': { + 'type': 'object', + 'properties': { + 'storageReplicationAcrossRegion': {}, + 'storageReplicationSize': {}, + 'storageReplicationFrequency': {}, + 'storageReplicationSource': {}, + 'storageReplicationDestination': {} + } + } + } + } + } +}; diff --git a/openecomp-ui/test/softwareProduct/networks/SoftwareProductNetworksView.test.js b/openecomp-ui/test/softwareProduct/networks/SoftwareProductNetworksView.test.js new file mode 100644 index 0000000000..a7f7b2b0c2 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/networks/SoftwareProductNetworksView.test.js @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import {mapStateToProps} from 'sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js'; +import SoftwareProductNetworksView from 'sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx'; +import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js'; + +describe('SoftwareProductNetworks Mapper and View Classes', () => { + it ('mapStateToProps mapper exists', () => { + expect(mapStateToProps).toExist(); + }); + + it ('mapStateToProps data test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + const networksList = [ + { + name:'dummy_net_1', + dhcp:true, + 'id':'7F60CD390458421DA588AF4AD217B93F' + }, + { + name:'dummy_net_2', + dhcp:true, + 'id':'AD217B93F7F60CD390458421DA588AF4' + } + ]; + + var obj = { + softwareProduct: { + softwareProductEditor: { + data:currentSoftwareProduct + }, + softwareProductNetworks: + { + networksList + } + } + }; + var results = mapStateToProps(obj); + expect(results.networksList,).toExist(); + }); + + it ('view simple test', () => { + + const currentSoftwareProduct = { + name: 'VSp', + description: 'dfdf', + vendorName: 'V1', + vendorId: '97B3E2525E0640ACACF87CE6B3753E80', + category: 'resourceNewCategory.application l4+', + subCategory: 'resourceNewCategory.application l4+.database', + id: 'D4774719D085414E9D5642D1ACD59D20', + version: '0.10', + viewableVersions: ['0.1', '0.2'], + status: versionStatusEnum.CHECK_OUT_STATUS, + lockingUser: 'cs0008' + }; + + const networksList = [ + { + name:'dummy_net_1', + dhcp:true, + 'id':'7F60CD390458421DA588AF4AD217B93F' + }, + { + name:'dummy_net_2', + dhcp:true, + 'id':'AD217B93F7F60CD390458421DA588AF4' + } + ]; + + const versionControllerData = { + version: '1', + viewableVersions: [], + status: 'locked', + isCheckedOut: true + }; + + var renderer = TestUtils.createRenderer(); + renderer.render(); + var renderedOutput = renderer.getRenderOutput(); + expect(renderedOutput).toExist(); + + }); + + + +}); diff --git a/openecomp-ui/test/softwareProduct/networks/softwareProductNetworksActionHelper.test.js b/openecomp-ui/test/softwareProduct/networks/softwareProductNetworksActionHelper.test.js new file mode 100644 index 0000000000..2920803c64 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/networks/softwareProductNetworksActionHelper.test.js @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import SoftwareProductNetworksActionHelper from 'sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js'; + +const softwareProductId = '123'; + +describe('Software Product Networks ActionHelper Tests', function () { + it('Get Software Products Networks List', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const networksList = [ + { + name:'dummy_net_1', + dhcp:true, + 'id':'7F60CD390458421DA588AF4AD217B93F' + }, + { + name:'dummy_net_2', + dhcp:true, + 'id':'AD217B93F7F60CD390458421DA588AF4' + } + ]; + + deepFreeze(networksList); + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductNetworks.networksList', networksList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`/onboarding-api/v1.0/vendor-software-products/${softwareProductId}/networks`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return {results: networksList}; + }); + + return SoftwareProductNetworksActionHelper.fetchNetworksList(store.dispatch, {softwareProductId}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + + }); +}); diff --git a/openecomp-ui/test/softwareProduct/processes/test.js b/openecomp-ui/test/softwareProduct/processes/test.js new file mode 100644 index 0000000000..73f22a7898 --- /dev/null +++ b/openecomp-ui/test/softwareProduct/processes/test.js @@ -0,0 +1,459 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {expect} from 'chai'; +import deepFreeze from 'deep-freeze'; +import mockRest from 'test-utils/MockRest.js'; +import {cloneAndSet} from 'test-utils/Util.js'; +import {storeCreator} from 'sdc-app/AppStore.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import SoftwareProductProcessesActionHelper from 'sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js'; + +const softwareProductId = '123'; + +describe('Software Product Processes Module Tests', function () { + + let restPrefix = ''; + + before(function() { + restPrefix = Configuration.get('restPrefix'); + deepFreeze(restPrefix); + }); + + //** + //** ADD + //** + it('Add Software Products Processes', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductPostRequest = { + name: 'Pr1', + description: 'string' + }; + const softwareProductProcessToAdd = { + name: 'Pr1', + description: 'string' + }; + const softwareProductProcessFromResponse = 'ADDED_ID'; + const softwareProductProcessAfterAdd = { + ...softwareProductProcessToAdd, + id: softwareProductProcessFromResponse + }; + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesList', [softwareProductProcessAfterAdd]); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes`); + expect(data).to.deep.equal(softwareProductPostRequest); + expect(options).to.equal(undefined); + return { + returnCode: 'OK', + value: softwareProductProcessFromResponse + }; + }); + + return SoftwareProductProcessesActionHelper.saveProcess(store.dispatch, + { + softwareProductId: softwareProductId, + previousProcess: null, + process: softwareProductProcessToAdd + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Add Software Products Processes with uploaded file', () => { + + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductPostRequest = { + name: 'Pr1', + description: 'string' + }; + const softwareProductProcessToAdd = { + name: 'Pr1', + description: 'string', + formData: { + name: 'new artifact name' + } + }; + const softwareProductProcessFromResponse = 'ADDED_ID'; + const softwareProductProcessAfterAdd = { + ...softwareProductProcessToAdd, + id: softwareProductProcessFromResponse + }; + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesList', [softwareProductProcessAfterAdd]); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes`); + expect(data).to.deep.equal(softwareProductPostRequest); + expect(options).to.equal(undefined); + return { + returnCode: 'OK', + value: softwareProductProcessFromResponse + }; + }); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes/${softwareProductProcessAfterAdd.id}/upload`); + expect(data).to.deep.equal(softwareProductProcessToAdd.formData); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductProcessesActionHelper.saveProcess(store.dispatch, + { + softwareProductId: softwareProductId, + previousProcess: null, + process: softwareProductProcessToAdd + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + //** + //** UPDATE + //** + it('Update Software Products Processes', () => { + const softwareProductProcessesList = [ + { + name: 'Pr1', + description: 'string', + id: 'EBADF561B7FA4A788075E1840D0B5971', + artifactName: 'artifact' + } + ]; + deepFreeze(softwareProductProcessesList); + + const store = storeCreator({ + softwareProduct: { + softwareProductProcesses: { + processesList: softwareProductProcessesList + } + } + }); + deepFreeze(store.getState()); + + const toBeUpdatedProcessId = softwareProductProcessesList[0].id; + const previousProcessData = softwareProductProcessesList[0]; + const processUpdateData = { + ...softwareProductProcessesList[0], + name: 'Pr1_UPDATED', + description: 'string_UPDATED' + }; + deepFreeze(processUpdateData); + + const processPutRequest = { + name: 'Pr1_UPDATED', + description: 'string_UPDATED' + }; + deepFreeze(processPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesList', [processUpdateData]); + + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes/${toBeUpdatedProcessId}`); + expect(data).to.deep.equal(processPutRequest); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductProcessesActionHelper.saveProcess(store.dispatch, + { + softwareProductId: softwareProductId, + previousProcess: previousProcessData, + process: processUpdateData + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Update Software Products Processes and uploaded file', () => { + const previousProcessData = { + id: 'EBADF561B7FA4A788075E1840D0B5971', + name: 'p1', + description: 'string', + artifactName: 'artifact' + }; + deepFreeze(previousProcessData); + + const store = storeCreator({ + softwareProduct: { + softwareProductProcesses: { + processesList: [previousProcessData] + } + } + }); + deepFreeze(store.getState()); + + const newProcessToUpdate = { + ...previousProcessData, + name: 'new name', + formData: { + name: 'new artifact name' + } + }; + deepFreeze(newProcessToUpdate); + + const newProcessToPutRequest = { + name: newProcessToUpdate.name, + description: previousProcessData.description + }; + deepFreeze(newProcessToPutRequest); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesList', [newProcessToUpdate]); + + mockRest.addHandler('save', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes/${previousProcessData.id}`); + expect(data).to.deep.equal(newProcessToPutRequest); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + mockRest.addHandler('create', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes/${previousProcessData.id}/upload`); + expect(data).to.deep.equal(newProcessToUpdate.formData); + expect(options).to.equal(undefined); + return {returnCode: 'OK'}; + }); + + return SoftwareProductProcessesActionHelper.saveProcess(store.dispatch, + { + softwareProductId: softwareProductId, + previousProcess: previousProcessData, + process: newProcessToUpdate + } + ).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + //** + //** GET + //** + it('Get Software Products Processes List', () => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const softwareProductProcessesList = [ + { + name: 'Pr1', + description: 'hjhj', + id: 'EBADF561B7FA4A788075E1840D0B5971', + artifactName: 'artifact' + }, + { + name: 'Pr1', + description: 'hjhj', + id: '2F47447D22DB4C53B020CA1E66201EF2', + artifactName: 'artifact' + } + ]; + + deepFreeze(softwareProductProcessesList); + + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesList', softwareProductProcessesList); + + mockRest.addHandler('fetch', ({options, data, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes`); + expect(data).to.deep.equal(undefined); + expect(options).to.equal(undefined); + return {results: softwareProductProcessesList}; + }); + + return SoftwareProductProcessesActionHelper.fetchProcessesList(store.dispatch, {softwareProductId}).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + //** + //** DELETE + //** + it('Delete Software Products Processes', () => { + const softwareProductProcessesList = [ + { + name: 'Pr1', + description: 'hjhj', + id: 'EBADF561B7FA4A788075E1840D0B5971', + artifactName: 'artifact' + } + ]; + + deepFreeze(softwareProductProcessesList); + const store = storeCreator({ + softwareProduct: { + softwareProductProcesses: { + processesList: softwareProductProcessesList + } + } + }); + + const processId = 'EBADF561B7FA4A788075E1840D0B5971'; + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesList', []); + + mockRest.addHandler('destroy', ({data, options, baseUrl}) => { + expect(baseUrl).to.equal(`${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/processes/${processId}`); + expect(data).to.equal(undefined); + expect(options).to.equal(undefined); + return { + results: { + returnCode: 'OK' + } + }; + }); + + return SoftwareProductProcessesActionHelper.deleteProcess(store.dispatch, { + process: softwareProductProcessesList[0], + softwareProductId + }).then(() => { + expect(store.getState()).to.deep.equal(expectedStore); + }); + }); + + it('Validating Software Products Processes Delete confirmation', done => { + const store = storeCreator(); + deepFreeze(store.getState()); + + let process = { + id: 'p_id', + name: 'p_name' + }; + deepFreeze(process); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processToDelete', process); + + SoftwareProductProcessesActionHelper.openDeleteProcessesConfirm(store.dispatch, {process}); + + setTimeout(function(){ + expect(store.getState()).to.deep.equal(expectedStore); + done(); + }, 100); + }); + + it('Validating Software Products Processes Cancel Delete', done => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processToDelete', false); + + SoftwareProductProcessesActionHelper.hideDeleteConfirm(store.dispatch); + + setTimeout(function(){ + expect(store.getState()).to.deep.equal(expectedStore); + done(); + }, 100); + }); + + //** + //** CREATE/EDIT + //** + it('Validating open Software Products Processes for create', done => { + const store = storeCreator(); + deepFreeze(store.getState()); + + let process = {}; + deepFreeze(process); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesEditor.data', process); + + SoftwareProductProcessesActionHelper.openEditor(store.dispatch); + + setTimeout(function(){ + expect(store.getState()).to.deep.equal(expectedStore); + done(); + }, 100); + }); + + it('Validating close Software Products Processes from editing mode', done => { + const store = storeCreator(); + deepFreeze(store.getState()); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesEditor', {}); + + SoftwareProductProcessesActionHelper.closeEditor(store.dispatch); + + setTimeout(function(){ + expect(store.getState()).to.deep.equal(expectedStore); + done(); + }, 100); + }); + + it('Validating open Software Products Processes for editing', done => { + const store = storeCreator(); + deepFreeze(store.getState()); + + let process = {name: 'aa', description: 'xx'}; + deepFreeze(process); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesEditor.data', process); + + SoftwareProductProcessesActionHelper.openEditor(store.dispatch, process); + + setTimeout(function(){ + expect(store.getState()).to.deep.equal(expectedStore); + done(); + }, 100); + }); + + it('Validating Software Products Processes dataChanged event', done => { + let process = {name: 'aa', description: 'xx'}; + deepFreeze(process); + + const store = storeCreator({ + softwareProduct: { + softwareProductProcesses: { + processesEditor: { + data: process + } + } + } + }); + deepFreeze(store.getState()); + + let deltaData = {name: 'bb'}; + deepFreeze(deltaData); + + let expectedProcess = {name: 'bb', description: 'xx'}; + deepFreeze(expectedProcess); + + const expectedStore = cloneAndSet(store.getState(), 'softwareProduct.softwareProductProcesses.processesEditor.data', expectedProcess); + + SoftwareProductProcessesActionHelper.processEditorDataChanged(store.dispatch, {deltaData}); + + setTimeout(function(){ + expect(store.getState()).to.deep.equal(expectedStore); + done(); + }, 100); + }); +}); + diff --git a/openecomp-ui/test/utils/errorResponseHandler.test.js b/openecomp-ui/test/utils/errorResponseHandler.test.js new file mode 100644 index 0000000000..fd9dec6a8d --- /dev/null +++ b/openecomp-ui/test/utils/errorResponseHandler.test.js @@ -0,0 +1,135 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import deepFreeze from 'deep-freeze'; +import expect from 'expect'; +import {cloneAndSet} from '../../test-utils/Util.js'; +import store from 'sdc-app/AppStore.js'; +import errorResponseHandler from 'nfvo-utils/ErrorResponseHandler.js'; + +describe('Error Response Handler Util', () => { + + beforeEach(function () { + deepFreeze(store.getState()); + }); + + it('validating error in policyException', done => { + let textStatus = '', errorThrown = ''; + let xhr = { + responseJSON: { + requestError: { + policyException: { + messageId: 'SVC4122', + text: 'Error: Invalid data.' + } + } + } + }; + deepFreeze(xhr); + + const errorNotification = { + type: 'error', title: 'Error: SVC4122', msg: 'Error: Invalid data.', timeout: undefined, + validationResponse: undefined + }; + const expectedStore = cloneAndSet(store.getState(), 'notification', errorNotification); + + errorResponseHandler(xhr, textStatus, errorThrown); + + setTimeout(function () { + expect(store.getState()).toEqual(expectedStore); + done(); + }, 100); + }); + + it('validating error in serviceException with variables', done => { + let textStatus = '', errorThrown = ''; + let xhr = { + responseJSON: { + requestError: { + serviceException: { + messageId: 'SVC4122', + text: "Error: Invalid artifact type '%1'.", + variables: ['newType'] + } + } + } + }; + deepFreeze(xhr); + + const errorNotification = { + type: 'error', title: 'Error: SVC4122', msg: 'Error: Invalid artifact type newType.', timeout: undefined, + validationResponse: undefined + }; + const expectedStore = cloneAndSet(store.getState(), 'notification', errorNotification); + + errorResponseHandler(xhr, textStatus, errorThrown); + + setTimeout(function () { + expect(store.getState()).toEqual(expectedStore); + done(); + }, 100); + }); + + it('validating error in response', done => { + let textStatus = '', errorThrown = ''; + let xhr = { + responseJSON: { + status: 'AA', + message: 'Error: Invalid data.' + } + }; + deepFreeze(xhr); + + const errorNotification = { + type: 'error', title: 'AA', msg: 'Error: Invalid data.', timeout: undefined, + validationResponse: undefined + }; + const expectedStore = cloneAndSet(store.getState(), 'notification', errorNotification); + + errorResponseHandler(xhr, textStatus, errorThrown); + + setTimeout(function () { + expect(store.getState()).toEqual(expectedStore); + done(); + }, 100); + }); + + it('validating error in request', done => { + let textStatus = '', errorThrown = ''; + let xhr = { + statusText: '500', + responseText: 'Internal server error.' + }; + deepFreeze(xhr); + + const errorNotification = { + type: 'error', title: '500', msg: 'Internal server error.', timeout: undefined, + validationResponse: undefined + }; + const expectedStore = cloneAndSet(store.getState(), 'notification', errorNotification); + + errorResponseHandler(xhr, textStatus, errorThrown); + + setTimeout(function () { + expect(store.getState()).toEqual(expectedStore); + done(); + }, 100); + }); +}); diff --git a/openecomp-ui/test/utils/restApiUtil.test.js b/openecomp-ui/test/utils/restApiUtil.test.js new file mode 100644 index 0000000000..2a5e69b02e --- /dev/null +++ b/openecomp-ui/test/utils/restApiUtil.test.js @@ -0,0 +1,149 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import expect from 'expect'; +import $ from 'jquery'; +import RestAPIUtil, {makeQueryParams} from 'src/nfvo-utils/RestAPIUtil'; + +const URL = 'http://bla.ble.blu/'; + +describe('RestAPIUtil Util class', () => { + + beforeEach(()=> { + $.ajax = (options) => options; + }); + + it('RestAPIUtil does exist', () => { + expect(RestAPIUtil).toExist(); + }); + + it('RestAPIUtil makeQueryParams does exist', () => { + expect(makeQueryParams).toExist(); + }); + + it('RestAPIUtil makeQueryParams params', () => { + const pageStart = 1, pageSize = 25; + const response = makeQueryParams({pagination: {pageStart, pageSize}}); + expect(response.pageStart).toBe(pageStart); + expect(response.pageSize).toBe(pageSize); + }); + + it('normal basic fetch', () => { + const response = RestAPIUtil.fetch(URL); + expect(response).toExist(); + }); + + it('no url', function () { + expect(function () { + RestAPIUtil.fetch(); + }).toThrow(/url/); + }); + + it('fetch with pagination', () => { + const pageStart = 1, pageSize = 25; + const response = RestAPIUtil.fetch(URL, {pagination: {pageStart, pageSize}}); + expect(response.pagination).toExist(); + expect(response.url).toInclude(`?pageStart=${pageStart}&pageSize=${pageSize}`); + }); + + it('fetch with sorting', () => { + const sortField = 'name', sortDir = 'ASCENDING'; + const response = RestAPIUtil.fetch(URL, {sorting: {sortField, sortDir}}); + expect(response.sorting).toExist(); + expect(response.url).toInclude(`?sortField=${sortField}&sortDir=${sortDir}`); + }); + + it('fetch with filtering', () => { + const baseFilter = [ + { + criterionValue: 'service', + fieldName: 'Brand', + operator: 'EQUALS', + type: 'STRING' + }, + { + criterionValue: 'resource', + fieldName: 'Brand', + operator: 'EQUALS', + type: 'STRING' + } + ]; + const response = RestAPIUtil.fetch(URL, {filtering: {filterCriteria: baseFilter, logicalRelation: 'OR'}}); + expect(response.filtering).toExist(); + expect(response.url).toInclude('?filter='); + }); + + it('fetch with qParams', () => { + const response = RestAPIUtil.fetch(URL, {qParams: {pageStart: 1, pageSize: 10}}); + expect(response.qParams).toExist(); + }); + + it('fetch with url on options', () => { + const response = RestAPIUtil.fetch(URL, {url:'12345', qParams: {pageStart: 1, pageSize: 10}}); + expect(response.qParams).toExist(); + }); + + it('fetch with url path param', () => { + let someData = 'data'; + const response = RestAPIUtil.fetch(`${URL}{someData}/`, {params: {someData}}); + expect(response.url).toInclude(`/${someData}/`); + }); + + it('fetch with url undefined path param', () => { + const response = RestAPIUtil.fetch(`${URL}{someData}/`, {params: {someData: undefined}}); + expect(response.url).toInclude('/undefined/'); + }); + + it('normal basic create', () => { + const response = RestAPIUtil.create(URL); + expect(response).toExist(); + }); + + it('create with FormData', () => { + let formData = new FormData(); + formData.append('username', 'Chris'); + const response = RestAPIUtil.create(URL, formData); + expect(response).toExist(); + }); + + it('create with FormData with md5', () => { + let formData = new FormData(); + formData.append('username', 'Chris'); + const response = RestAPIUtil.create(URL, formData, {md5: true}); + expect(response).toExist(); + }); + + it('create with file', () => { + let progressCallback = () => {}; + const response = RestAPIUtil.create(URL, {}, {progressCallback, fileSize: 123}); + expect(response).toExist(); + }); + + it('normal basic save', () => { + const response = RestAPIUtil.save(URL); + expect(response).toExist(); + }); + + it('normal basic delete', () => { + const response = RestAPIUtil.destroy(URL); + expect(response).toExist(); + }); + +}); diff --git a/openecomp-ui/test/utils/uuid.test.js b/openecomp-ui/test/utils/uuid.test.js new file mode 100644 index 0000000000..cd55baadea --- /dev/null +++ b/openecomp-ui/test/utils/uuid.test.js @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import uuid from 'src/nfvo-utils/UUID.js'; +import expect from 'expect'; + +describe('UUID', () => { + + it('function does exist', () => { + expect(uuid).toExist(); + }); + + it('generate UUID synchronously', () => { + let result = uuid(undefined, true); + expect(result).toExist(); + }); + + it('generate UUID synchronously with number', () => { + let result = uuid(5, true); + expect(result).toExist(); + }); + + it('generate UUID synchronously with number', () => { + let result = uuid(1, true); + expect(result).toExist(); + }); + + it('generate UUID asynchronously', done => { + uuid().then(result => { + expect(result).toExist(); + done(); + }); + }); + +}); diff --git a/openecomp-ui/tests.webpack.js b/openecomp-ui/tests.webpack.js new file mode 100644 index 0000000000..10b54fed83 --- /dev/null +++ b/openecomp-ui/tests.webpack.js @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var testContext = require.context('./test', true, /.test\.js$/); +testContext.keys().forEach(testContext); + +var utilsContext = require.context('./src/nfvo-utils', true, /\.js$/); +utilsContext.keys().forEach(utilsContext); + +var componentsContext = require.context('./src/nfvo-components', true, /\.(js|jsx)$/); +componentsContext.keys().forEach(componentsContext); + +var flowsCodeContext = require.context('./src/sdc-app/flows', true, /\.(js|jsx)$/); +flowsCodeContext.keys().forEach(flowsCodeContext); + +var onBoardingCodeContext = require.context('./src/sdc-app/onboarding', true, /\.(js|jsx)$/); +onBoardingCodeContext.keys().forEach(onBoardingCodeContext); + diff --git a/openecomp-ui/tools/gulp/deployment/gulpfile.js b/openecomp-ui/tools/gulp/deployment/gulpfile.js new file mode 100644 index 0000000000..99389108bb --- /dev/null +++ b/openecomp-ui/tools/gulp/deployment/gulpfile.js @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var gulp = require('gulp'); +var i18nUpdateTask = require('./tools/gulp/tasks/i18nUpdate'); + +gulp.task('i18nUpdate', function() { + + return i18nUpdateTask({ + warDir: process.cwd(), + lang: 'en' + }); +}); + +gulp.task('default', ['i18nUpdate']); + diff --git a/openecomp-ui/tools/gulp/deployment/package.json b/openecomp-ui/tools/gulp/deployment/package.json new file mode 100644 index 0000000000..3bad0374bf --- /dev/null +++ b/openecomp-ui/tools/gulp/deployment/package.json @@ -0,0 +1,23 @@ +{ + "name": "sdc-client-tools", + "version": "9.3.0", + "description": "Service Designer & Catalog Client Tools", + "dependencies": {}, + "devDependencies": { + "bluebird": "^2.10.1", + "gulp": "^3.9.0", + "gulp-rename": "^1.2.2", + "gulp-replace": "^0.5.4", + "prompt": "^0.2.14" + }, + "author": "ECOMP", + "license": "LicenseRef-LICENSE", + "scripts": { + "start": "gulp run", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "engines": { + "node": ">=0.12.7", + "npm": ">=2.11.3" + } +} diff --git a/openecomp-ui/tools/gulp/deployment/tools/gulp/tasks/i18nUpdate.js b/openecomp-ui/tools/gulp/deployment/tools/gulp/tasks/i18nUpdate.js new file mode 100644 index 0000000000..a3cae5b018 --- /dev/null +++ b/openecomp-ui/tools/gulp/deployment/tools/gulp/tasks/i18nUpdate.js @@ -0,0 +1,171 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var gulp, replace, rename, fs, prompt, Promise; + +function mergePromptOptions(options) { + + return new Promise(function(resolve, reject) { + var lang = options.lang; + var warDir = options.warDir; + prompt.start(); + prompt.get([ + { + description: 'Enter war directory', + default: warDir, + name: 'warDir' + }, + { + description: 'Enter locale.json parent directory name', + default: lang, + name: 'lang' + } + ], function (err, result) { + + if(err) { + reject(new Error('mergePromptOptions::>\n ' + err)); + return; + } + + var warDir = result.warDir; + var lang = result.lang; + + console.log('\nlocale.json parent directory name> "' + lang + '"'); + console.log('war director>"' + warDir + '"'); + + resolve({ + warDir: warDir, + lang: lang + }); + }); + }); +} + +function isBundleExists(path) { + return new Promise(function(resolve) { + fs.stat(path, function(err) { + resolve(null == err); + /*if null == err then file exists.*/ + }); + }); +} + +function copyEnglishBundle(enBundlePath, lang) { + return new Promise(function(resolve, reject) { + gulp.src(enBundlePath, {base: './'}) + .pipe(rename({basename: 'bundle_' + lang})) + .pipe(gulp.dest('./')) + .on('end', function() { + resolve(); + }) + .on('error', function(err) { + reject(new Error('copyEnglishBundle::>\n ' + err)); + }); + }); +} + +function getLocaleContent(localePath) { + + return new Promise(function(resolve, reject) { + fs.readFile(localePath, {encoding: 'utf-8'}, function(err,data){ + if(err) { + reject('getLocaleContent()::>\n ' + err); + return; + } + resolve(data); + }); + }); + +} + +function extractLocaleJsonContent(localeDataStr) { + + var localeJsonStrI18nStartIdx = localeDataStr.indexOf('I18N_IDENTIFIER_START'); + var localeJsonStrI18nEndIdx = localeDataStr.indexOf('I18N_IDENTIFIER_END'); + + if(-1 === localeJsonStrI18nStartIdx || -1 === localeJsonStrI18nEndIdx) { + return Promise.reject(new Error('extractLocaleJsonContent::> localeDataStr must contain %I18N_IDENTIFIER_START% and %I18N_IDENTIFIER_END%')); + } + + var localeJsonStr = localeDataStr.substring( + localeDataStr.indexOf('{', localeJsonStrI18nStartIdx), + localeDataStr.lastIndexOf('}', localeJsonStrI18nEndIdx) + 1 + ); + + try { + JSON.parse(localeJsonStr); + } catch(e) { + return Promise.reject(new Error('extractLocaleJsonContent::> localeDataStr must contain a valid json between %I18N_IDENTIFIER_START% and %I18N_IDENTIFIER_END%=>' + e)); + } + + return Promise.resolve(localeJsonStr); +} + +function setBundleLocaleContent(bundlePath, localeJsonStr) { + return new Promise(function(resolve, reject) { + gulp.src(bundlePath, {base: './'}) + .pipe(replace(/I18N_IDENTIFIER_START(.|[\r\n])*?I18N_IDENTIFIER_END/i, function(expr) { + return expr.substring(0, expr.indexOf('{')) + localeJsonStr + expr.substring(expr.lastIndexOf('}') + 1); + })) + .pipe(gulp.dest('./')) + .on('end', function() { + resolve(); + }) + .on('error', function(err) { + reject(new Error('setBundleLocaleContent::>\n ' + err)); + }); + }); +} + + +function update(options) { + + gulp = require('gulp'); + replace = require('gulp-replace'); + rename = require('gulp-rename'); + fs = require('fs'); + prompt = require('prompt'); + Promise = require('bluebird'); + + return mergePromptOptions(options).then(function(mergedOptions) { + var lang = mergedOptions.lang; + var warDir = mergedOptions.warDir; + + var bundlePath = warDir + '/js/bundle_' + lang + '.js'; + var localePath = warDir + '/i18n/' + lang + '/locale.json'; + + return isBundleExists(bundlePath) + .then(function(isBundleExist) { + var englishBundlePath; + if(!isBundleExist) { + englishBundlePath = warDir + '/js/bundle_en.js'; + return copyEnglishBundle(englishBundlePath, lang); + } + }) + .then(getLocaleContent.bind(null, localePath)) + .then(extractLocaleJsonContent) + .then(setBundleLocaleContent.bind(null, bundlePath)); + }); + +} + + + +module.exports = update; diff --git a/openecomp-ui/tools/gulp/tasks/i18n.js b/openecomp-ui/tools/gulp/tasks/i18n.js new file mode 100644 index 0000000000..38b2a02dcc --- /dev/null +++ b/openecomp-ui/tools/gulp/tasks/i18n.js @@ -0,0 +1,115 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var gulp = require('gulp'); +var fs = require('fs'); +var replace = require('gulp-replace'); +var clean = require('gulp-clean'); +var mkdirp = require('mkdirp'); + +/** + * + * @param options.outputPath + * @param options.localesPath + * @param options.lang = options.lang + * + * @returns {string} + */ +function composeLocalesDirPath(options) { + return options.outputPath + options.localesPath + options.lang; +} + +/** + * + * @param options.outputPath + * @param options.localesPath + * @param options.lang + * + * @returns {string} + */ +function composeLocaleFilePath(options) { + return composeLocalesDirPath(options) + '/locale.json'; +} + + +/** + * @param options + * @param options.outputPath + * @param options.localesPath + * @param options.lang = options.lang + * + */ +function ensureLocalesDir(options) { + + return new Promise(function (resolve, reject) { + mkdirp(composeLocalesDirPath(options), function (err) { + if (err) { + reject(err); + } + else { + resolve(); + } + }); + }); + +} + +/** + * + * @param options + * @param options.outputPath + * @param options.localesPath + * @param options.lang = options.lang + * + */ +function i18nTask(options) { + + var i18nJson = {}; + + function addWord(expr) { + var word = expr.substring('i18n(\''.length, expr.length - 1); + i18nJson[word] = word; + return expr; + } + + return ensureLocalesDir(options).then(function () { + return new Promise(function(resolve, reject) { + gulp.src(options.outputPath + '**/*.js', {base: './'}) + .pipe(replace(/i18n\('.*?'/g, addWord)) + .pipe(clean()) + .pipe(gulp.dest('./')) + .on('end', function () { + + var i18nJsonWrapper = { dataWrapperArr: ["I18N_IDENTIFIER_START", i18nJson, "I18N_IDENTIFIER_END"] , i18nDataIdx: 1}; + + fs.writeFile(composeLocaleFilePath(options), JSON.stringify(i18nJsonWrapper), function (err) { + if (err) { + reject(err); + } + else resolve(); + }); + }).on('error', function (err) { + reject(err); + }); + }); + }); +} + +module.exports = i18nTask; diff --git a/openecomp-ui/tools/gulp/tasks/prod.js b/openecomp-ui/tools/gulp/tasks/prod.js new file mode 100644 index 0000000000..d66b841d2a --- /dev/null +++ b/openecomp-ui/tools/gulp/tasks/prod.js @@ -0,0 +1,100 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +var gulp, replace, Promise, webpack, webpackProductionConfig; + +var supportedLanguages = ['en']; + +function start(options) { + + var promises = [buildIndex(options)]; + supportedLanguages.forEach(function (lang) { + promises.push(bundleJS(options, lang)); + }); + return Promise.all(promises); +} + +function bundleJS(options, lang) { + return new Promise(function (resolve, reject) { + var prodConfig = webpackProductionConfig; + prodConfig.resolve.alias.i18nJson = options.outDir + '/i18n/' + lang + '/locale.json'; + prodConfig.output.filename = jsFileByLang(options.outFileName, lang); + webpack(prodConfig, function (err, stats) { + console.log('[webpack:build]', stats.toString()); + if (err || stats.hasErrors()) { + console.log('bundleJS : Failure!!', '\n -language: ', lang); + reject(err || stats.toJson().errors); + } + else { + console.log('bundleJS : Done', '\n -language: ', lang); + resolve(); + } + }); + }); +} + +function buildIndex(options) { + + return new Promise(function (resolve, reject) { + + var stream = gulp.src(options.outDir + '/index.html'); + + stream.pipe(replace(/\/\/(.|[\r\n])*?/g, ''))//in script occurrences. + .pipe(replace(/(.|[\r\n])*?/g, ''))//out of script occurrences. + .pipe(replace(/)?/g, '')) + .pipe(replace(/\/\/(.|[\r\n])*?/g, supportedLanguages.map(function (val) { + return "'" + val + "'"; + }).toString())) + .pipe(gulp.dest(options.outDir)) + .on('end', function () { + console.log('buildIndex : Done'); + resolve(); + }) + .on('error', function (e) { + console.log('buildIndex : Failure!!'); + reject(e); + }); + }); + +} + +function jsFileByLang(fileName, lang) { + return fileName.replace(/.js$/, '_' + lang + '.js'); +} + +/** + * @param options + * @param options.outFileName optional + */ +function prodTask(options) { + + gulp = require('gulp'); + replace = require('gulp-replace'); + Promise = require('bluebird'); + webpack = require('webpack'); + webpackProductionConfig = options.webpackProductionConfig; + + return start({ + outFileName: options.outFileName || '[name].js', + outDir: options.outDir + }); +} + +module.exports = prodTask; diff --git a/openecomp-ui/tools/webpack/config-json-loader/index.js b/openecomp-ui/tools/webpack/config-json-loader/index.js new file mode 100644 index 0000000000..bf34533f67 --- /dev/null +++ b/openecomp-ui/tools/webpack/config-json-loader/index.js @@ -0,0 +1,26 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +module.exports = function(content) { + var config = JSON.parse(content); + var build = process.env.BUILD_NUMBER || '0'; + config.build = build; + return JSON.stringify(config); +}; diff --git a/openecomp-ui/webapp-heat-validation/META-INF/context.xml b/openecomp-ui/webapp-heat-validation/META-INF/context.xml new file mode 100644 index 0000000000..2ada7fc48f --- /dev/null +++ b/openecomp-ui/webapp-heat-validation/META-INF/context.xml @@ -0,0 +1,4 @@ + + + + diff --git a/openecomp-ui/webapp-heat-validation/WEB-INF/jetty-web.xml b/openecomp-ui/webapp-heat-validation/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..bc3bfa62f0 --- /dev/null +++ b/openecomp-ui/webapp-heat-validation/WEB-INF/jetty-web.xml @@ -0,0 +1,8 @@ + + + + + /heat-validation + diff --git a/openecomp-ui/webapp-heat-validation/WEB-INF/rewrite.config b/openecomp-ui/webapp-heat-validation/WEB-INF/rewrite.config new file mode 100644 index 0000000000..b5e1a31493 --- /dev/null +++ b/openecomp-ui/webapp-heat-validation/WEB-INF/rewrite.config @@ -0,0 +1,2 @@ +RewriteRule ^.*\..*$ - [L] +RewriteRule ^.*$ /heat.html [L] diff --git a/openecomp-ui/webapp-heat-validation/WEB-INF/web.xml b/openecomp-ui/webapp-heat-validation/WEB-INF/web.xml new file mode 100644 index 0000000000..f84519eee3 --- /dev/null +++ b/openecomp-ui/webapp-heat-validation/WEB-INF/web.xml @@ -0,0 +1,51 @@ + + + + Heat-Validation + + + heat.html + + + + js + org.eclipse.jetty.servlet.DefaultServlet + + + cacheControl + public, no-cache + + + etags + true + + + + + js + *.js + + + + resources + org.eclipse.jetty.servlet.DefaultServlet + + + cacheControl + public, max-age=31536000 + + + etags + true + + + + + resources + / + + + diff --git a/openecomp-ui/webapp-heat-validation/heat-validation.xml b/openecomp-ui/webapp-heat-validation/heat-validation.xml new file mode 100644 index 0000000000..404dcf23f3 --- /dev/null +++ b/openecomp-ui/webapp-heat-validation/heat-validation.xml @@ -0,0 +1,45 @@ + + + + + + + /heat-validation + /heat-validation.war + + + + org.eclipse.jetty.websocket.jsr356 + true + + + + + 2048 + + + + + + + + Test Realm + /etc/realm.properties + + + + + + + + true + + + true + + + + diff --git a/openecomp-ui/webapp-onboarding/META-INF/context.xml b/openecomp-ui/webapp-onboarding/META-INF/context.xml new file mode 100644 index 0000000000..2ada7fc48f --- /dev/null +++ b/openecomp-ui/webapp-onboarding/META-INF/context.xml @@ -0,0 +1,4 @@ + + + + diff --git a/openecomp-ui/webapp-onboarding/WEB-INF/jetty-web.xml b/openecomp-ui/webapp-onboarding/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..0a9d33a940 --- /dev/null +++ b/openecomp-ui/webapp-onboarding/WEB-INF/jetty-web.xml @@ -0,0 +1,8 @@ + + + + + /onboarding + diff --git a/openecomp-ui/webapp-onboarding/WEB-INF/rewrite.config b/openecomp-ui/webapp-onboarding/WEB-INF/rewrite.config new file mode 100644 index 0000000000..90893bb545 --- /dev/null +++ b/openecomp-ui/webapp-onboarding/WEB-INF/rewrite.config @@ -0,0 +1,2 @@ +RewriteRule ^.*\..*$ - [L] +RewriteRule ^.*$ /index.html [L] diff --git a/openecomp-ui/webapp-onboarding/WEB-INF/web.xml b/openecomp-ui/webapp-onboarding/WEB-INF/web.xml new file mode 100644 index 0000000000..6dd619fbbc --- /dev/null +++ b/openecomp-ui/webapp-onboarding/WEB-INF/web.xml @@ -0,0 +1,51 @@ + + + + ASDC + + + index.html + + + + js + org.eclipse.jetty.servlet.DefaultServlet + + + cacheControl + public, no-cache + + + etags + true + + + + + js + *.js + + + + resources + org.eclipse.jetty.servlet.DefaultServlet + + + cacheControl + public, max-age=31536000 + + + etags + true + + + + + resources + / + + + diff --git a/openecomp-ui/webapp-onboarding/onboarding.xml b/openecomp-ui/webapp-onboarding/onboarding.xml new file mode 100644 index 0000000000..50df896a4b --- /dev/null +++ b/openecomp-ui/webapp-onboarding/onboarding.xml @@ -0,0 +1,45 @@ + + + + + + + /onboarding + /onboarding-fe.war + + + + org.eclipse.jetty.websocket.jsr356 + true + + + + + 2048 + + + + + + + + Test Realm + /etc/realm.properties + + + + + + + + true + + + true + + + + diff --git a/openecomp-ui/webpack.config.js b/openecomp-ui/webpack.config.js new file mode 100644 index 0000000000..2cccba8f0f --- /dev/null +++ b/openecomp-ui/webpack.config.js @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +'use strict'; + +var path = require('path'); +var webpack = require('webpack'); + +var localDevConfig = {}; +try { + localDevConfig = require('./devConfig'); +} catch(e) {} +var devConfig = Object.assign({}, require('./devConfig.defaults'), localDevConfig); +var devPort = process.env.PORT || devConfig.port; +var latestProgress = 0; + +module.exports = { + devtool: 'eval-source-map', + entry: { + bundle: [ + 'sdc-app/sdc.app.jsx', + `webpack-dev-server/client?http://localhost:${devPort}`, + 'webpack/hot/only-dev-server' + ], + 'punch-outs': [ + 'sdc-app/punch-outs.js', + `webpack-dev-server/client?http://localhost:${devPort}`, + 'webpack/hot/only-dev-server' + ], + 'heat-validation': [ + 'sdc-app/heatValidation.app.jsx', + `webpack-dev-server/client?http://localhost:${devPort}`, + 'webpack/hot/only-dev-server' + ] + }, + resolve: { + root: [path.resolve('.')], + alias: { + i18nJson: 'nfvo-utils/i18n/locale.json', + 'nfvo-utils': 'src/nfvo-utils', + 'nfvo-components': 'src/nfvo-components', + 'sdc-app': 'src/sdc-app' + } + }, + output: { + path: path.join(__dirname, 'dist/dev'), + publicPath: `http://localhost:${devPort}/onboarding/`, + filename: '[name].js' + }, + devServer: { + port: devPort, + historyApiFallback: true, + publicPath: `http://localhost:${devPort}/onboarding/`, + contentBase: path.join(__dirname, 'dist/dev'), + hot: true, + progress: true, + inline: true, + debug: true, + stats: { + colors: true + } + }, + module: { + preLoaders: [ + {test: /\.(js|jsx)$/, loader: 'source-map-loader', exclude: /node_modules/} + ], + loaders: [ + {test: /\.(js|jsx)$/, loaders: ['react-hot', 'babel-loader', 'eslint-loader'], exclude: /node_modules/}, + {test: /\.(css|scss)$/, loaders: ['style', 'css?sourceMap', 'sass?sourceMap']}, + + // required for font icons + {test: /\.(woff|woff2)(\?.*)?$/, loader: 'url-loader?limit=16384&mimetype=application/font-woff' }, + {test: /\.(ttf|eot|otf)(\?.*)?$/, loader: 'file-loader' }, + {test: /\.(png|jpg|svg)(\?.*)?$/, loader: 'url-loader?limit=16384'}, + + {test: /\.json$/, loaders: ['json']}, + {test: /\.html$/, loaders: ['html']} + ] + }, + eslint: { + configFile: './.eslintrc', + emitError: true, + emitWarning: true + }, + plugins: [ + new webpack.DefinePlugin({ + DEV: true, + DEBUG: true + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.ProgressPlugin(function (percentage, msg) { + if (percentage == 0) { + latestProgress = 0; + console.log(); //new line + } + var progressVal = (percentage * 100).toFixed(0); + if (progressVal > latestProgress) { + latestProgress = progressVal + //process.stdout.clearLine(); + process.stdout.write(msg + ' ' + progressVal + '%\r'); + } + }) + ] + +}; diff --git a/pom.xml b/pom.xml index 27b32b3e09..2a847cdb73 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ sdc-os-chef openecomp-be - + openecomp-ui diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz index 4a5217f2cc..0962975982 100644 Binary files a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz and b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz differ