Przeglądaj źródła

Coriolis with Atomic design implementation

Includes:
Alt Flux data flow framework
Jest testing framework
Storybook
Styled Components
Sergiu Miclea 8 lat temu
rodzic
commit
0d39a85837
100 zmienionych plików z 1495 dodań i 5893 usunięć
  1. 39 6
      .babelrc
  2. 0 19
      .csslintrc
  3. 20 0
      .editorconfig
  4. 1 1
      .eslintignore
  5. 40 18
      .eslintrc
  6. 5 10
      .gitignore
  7. 0 6
      .jscsrc
  8. 8 16
      Dockerfile
  9. 1 1
      LICENSE
  10. 5 6
      README.md
  11. 84 129
      package.json
  12. 0 26
      preprocessor.js
  13. 16 0
      private/jest/componentsMock.js
  14. 1 0
      private/jest/fileMock.js
  15. 4 0
      private/jest/setupTests.js
  16. 3 0
      private/jest/shim.js
  17. 31 0
      private/storybook/Decorator.jsx
  18. 18 0
      private/storybook/config.js
  19. 11 0
      private/storybook/webpack.config.js
  20. BIN
      public/android-chrome-192x192.png
  21. BIN
      public/android-chrome-256x256.png
  22. BIN
      public/apple-touch-icon.png
  23. 9 0
      public/browserconfig.xml
  24. BIN
      public/favicon-16x16.png
  25. BIN
      public/favicon-32x32.png
  26. BIN
      public/favicon.ico
  27. 20 0
      public/index.html
  28. 18 0
      public/manifest.json
  29. BIN
      public/mstile-150x150.png
  30. 77 0
      server.js
  31. 0 33
      src/__tests__/LoadingIcon.test.js
  32. 0 32
      src/__tests__/LoginPage.test.js
  33. 0 19
      src/__tests__/ProgressBar.test.js
  34. 0 209
      src/__tests__/__snapshots__/LoadingIcon.test.js.snap
  35. 0 77
      src/__tests__/__snapshots__/LoginPage.test.js.snap
  36. 0 39
      src/__tests__/__snapshots__/ProgressBar.test.js.snap
  37. 0 0
      src/actions/.gitignore
  38. 0 232
      src/actions/ConnectionsActions/ConnectionsActions.js
  39. 0 6
      src/actions/ConnectionsActions/package.json
  40. 128 0
      src/actions/EndpointActions.js
  41. 134 0
      src/actions/InstanceActions.js
  42. 108 0
      src/actions/MigrationActions.js
  43. 0 359
      src/actions/MigrationActions/MigrationActions.js
  44. 0 6
      src/actions/MigrationActions/package.json
  45. 16 39
      src/actions/NetworkActions.js
  46. 7 11
      src/actions/NotificationActions.js
  47. 0 6
      src/actions/NotificationActions/package.json
  48. 38 0
      src/actions/ProjectActions.js
  49. 70 0
      src/actions/ProviderActions.js
  50. 178 0
      src/actions/ReplicaActions.js
  51. 105 0
      src/actions/ScheduleActions.js
  52. 127 0
      src/actions/UserActions.js
  53. 0 171
      src/actions/UserActions/UserActions.js
  54. 0 6
      src/actions/UserActions/package.json
  55. 91 0
      src/actions/WizardActions.js
  56. 0 22
      src/actions/WizardActions/WizardActions.js
  57. 0 6
      src/actions/WizardActions/package.json
  58. 3 5
      src/alt.js
  59. 0 122
      src/client.js
  60. 0 789
      src/components/AddCloudConnection/AddCloudConnection.js
  61. 0 234
      src/components/AddCloudConnection/AddCloudConnection.scss
  62. 0 6
      src/components/AddCloudConnection/package.json
  63. 0 6
      src/components/ApiCaller/package.json
  64. 79 0
      src/components/App.jsx
  65. 0 109
      src/components/App/App.js
  66. 0 580
      src/components/App/App.scss
  67. 0 6
      src/components/App/package.json
  68. 0 72
      src/components/CloudConnection/CloudConnection.js
  69. 0 6
      src/components/CloudConnection/package.json
  70. 0 98
      src/components/CloudConnectionAuth/CloudConnectionAuth.js
  71. 0 71
      src/components/CloudConnectionAuth/CloudConnectionAuth.scss
  72. 0 6
      src/components/CloudConnectionAuth/package.json
  73. 0 196
      src/components/CloudConnectionDetail/CloudConnectionDetail.js
  74. 0 86
      src/components/CloudConnectionDetail/CloudConnectionDetail.scss
  75. 0 6
      src/components/CloudConnectionDetail/package.json
  76. 0 236
      src/components/CloudConnectionsView/CloudConnectionsView.js
  77. 0 126
      src/components/CloudConnectionsView/CloudConnectionsView.scss
  78. 0 6
      src/components/CloudConnectionsView/package.json
  79. 0 159
      src/components/CloudItem/CloudItem.js
  80. 0 53
      src/components/CloudItem/CloudItem.scss
  81. 0 6
      src/components/CloudItem/package.json
  82. 0 73
      src/components/ConfirmationDialog/ConfirmationDialog.js
  83. 0 6
      src/components/ConfirmationDialog/package.json
  84. 0 6
      src/components/ContactPage/package.json
  85. 0 48
      src/components/ContentPage/ContentPage.js
  86. 0 6
      src/components/ContentPage/package.json
  87. 0 146
      src/components/DropdownButton/DropdownButton.js
  88. 0 115
      src/components/DropdownButton/DropdownButton.scss
  89. 0 6
      src/components/DropdownButton/package.json
  90. 0 174
      src/components/EditProfile/EditProfile.js
  91. 0 164
      src/components/EditProfile/EditProfile.scss
  92. 0 6
      src/components/EditProfile/package.json
  93. 0 108
      src/components/EndpointLink/EndpointLink.js
  94. 0 6
      src/components/EndpointLink/package.json
  95. 0 216
      src/components/EndpointList/EndpointList.js
  96. 0 186
      src/components/EndpointList/EndpointList.scss
  97. 0 6
      src/components/EndpointList/package.json
  98. 0 74
      src/components/EndpointUsage/EndpointUsage.js
  99. 0 6
      src/components/EndpointUsage/package.json
  100. 0 46
      src/components/ErrorPage/ErrorPage.js

+ 39 - 6
.babelrc

@@ -1,11 +1,44 @@
 {
 {
   "presets": [
   "presets": [
-    "es2015",
-    "stage-0",
+    [
+      "env",
+      {
+        "modules": false
+      }
+    ],
     "react",
     "react",
-    "babel-preset-jest"
+    "stage-1"
   ],
   ],
   "plugins": [
   "plugins": [
-    "transform-runtime"
-  ]
-}
+    "react-hot-loader/babel"
+  ],
+  "env": {
+    "development": {
+      "plugins": [
+        "transform-es2015-modules-commonjs",
+        [
+          "styled-components",
+          {
+            "minify": false
+          }
+        ]
+      ]
+    },
+    "test": {
+      "plugins": [
+        "transform-es2015-modules-commonjs"
+      ]
+    },
+    "production": {
+      "plugins": [
+        "transform-react-remove-prop-types",
+        [
+          "styled-components",
+          {
+            "displayName": false
+          }
+        ]
+      ]
+    }
+  }
+}

+ 0 - 19
.csslintrc

@@ -1,19 +0,0 @@
-{
-  "adjoining-classes": false,
-  "box-sizing": false,
-  "box-model": false,
-  "compatible-vendor-prefixes": false,
-  "floats": false,
-  "font-sizes": false,
-  "gradients": false,
-  "important": false,
-  "known-properties": false,
-  "outline-none": false,
-  "qualified-headings": false,
-  "regex-selectors": false,
-  "shorthand": false,
-  "text-indent": false,
-  "unique-headings": false,
-  "universal-selector": false,
-  "unqualified-attributes": false
-}

+ 20 - 0
.editorconfig

@@ -0,0 +1,20 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 2
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false

+ 1 - 1
.eslintignore

@@ -1 +1 @@
-src/__tests__
+server.js

+ 40 - 18
.eslintrc

@@ -1,26 +1,48 @@
 {
 {
   "parser": "babel-eslint",
   "parser": "babel-eslint",
-  "extends": "airbnb",
+  "extends": [
+    "airbnb"
+  ],
+  "env": {
+    "browser": true,
+    "jest": true
+  },
   "globals": {
   "globals": {
-    "__DEV__": true
+    "__DEV__": true,
+    "__PROD__": true,
+    "__DEBUG__": true,
+    "__COVERAGE__": true,
+    "__BASENAME__": true
+  },
+  "settings": {
+    "import/resolver": {
+      "webpack": {
+        "config": "webpack.config.js"
+      }
+    }
   },
   },
   "rules": {
   "rules": {
-    "no-confusing-arrow": 0,
-    "react/jsx-quotes": 0,
-    "jsx-quotes": [2, "prefer-double"],
-    "comma-dangle": 0,
-    "semi": 0,
-    "quotes": 0,
-    "eqeqeq": 0,
+    "semi": [2, "never"],
+    "comma-dangle": [2, "always-multiline"],
+    "class-methods-use-this": 0,
+    "max-len": 0,
     "prefer-const": 0,
     "prefer-const": 0,
-    "max-len": ["error", 120],
-    "react/jsx-no-bind": 0,
-    "prefer-template": 0,
-    "object-shorthand": 0,
+    "arrow-parens": 0,
+    "react/prefer-stateless-function": 0,
+    "react/no-array-index-key": 0,
     "no-param-reassign": 0,
     "no-param-reassign": 0,
-    "no-else-return": 0,
-    "guard-for-in": 0,
-    "no-nested-ternary": 0,
-    "no-case-declarations": 0
+    "no-shadow": 0,
+    "arrow-body-style": 0,
+    "global-require": 0,
+    "no-unused-expressions": 0,
+    "no-confusing-arrow": 0,
+    "import/no-dynamic-require": 0,
+    "import/no-unresolved": 0,
+    "import/extensions": 0,
+    "import/no-extraneous-dependencies": 0,
+    "import/prefer-default-export": 0,
+    "react/require-default-props": 0,
+    "react/forbid-prop-types": 0,
+    "jsx-a11y/href-no-hash": 0
   }
   }
-}
+}

+ 5 - 10
.gitignore

@@ -1,11 +1,6 @@
-# Include your project-specific ignores in this file
-# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
-
-build
+.DS_Store
+.happypack
+dist
+*.log
 node_modules
 node_modules
-ncp-debug.log
-npm-debug.log
-.idea
-yarn.lock
-src/config.js
-.DS_Store
+.vscode

+ 0 - 6
.jscsrc

@@ -1,6 +0,0 @@
-{
-  "preset": "airbnb",
-  "excludeFiles": ["build/**", "node_modules/**"],
-  "validateQuoteMarks": null,
-  "disallowSpacesInsideTemplateStringPlaceholders": null
-}

+ 8 - 16
Dockerfile

@@ -1,21 +1,13 @@
-FROM node:7.9.0-alpine
+FROM node:9.2.0
 
 
-# Set a working directory
-WORKDIR /usr/src/app
-
-COPY ./build/package.json .
-COPY yarn.lock .
+RUN curl -o- -L https://yarnpkg.com/install.sh | bash
 
 
-# Set CORIOLIS_URL
-ENV CORIOLIS_URL http://127.0.0.1
-COPY ./src/config.sample.js ./src/config.js
-
-# Install Node.js dependencies
-RUN yarn install --production --no-progress
+WORKDIR /usr/src/app
+COPY . .
 
 
-# Copy application files
-COPY ./build .
+RUN yarn install --production
+RUN yarn build
 
 
-EXPOSE 3000
+ENTRYPOINT [ "node", "server.js" ]
 
 
-CMD [ "node", "server.js" ]
+EXPOSE 3000

+ 1 - 1
LICENSE

@@ -2,7 +2,7 @@ All files in this repository are licensed as follows. If you contribute
 to this repository, it is assumed that you license your contribution
 to this repository, it is assumed that you license your contribution
 under the same license unless you state otherwise.
 under the same license unless you state otherwise.
 
 
-All files Copyright (C) 2016 Cloudbase Solutions Srl unless otherwise
+All files Copyright (C) 2017 Cloudbase Solutions Srl unless otherwise
 specified in the file.
 specified in the file.
 
 
                     GNU AFFERO GENERAL PUBLIC LICENSE
                     GNU AFFERO GENERAL PUBLIC LICENSE

+ 5 - 6
README.md

@@ -3,14 +3,13 @@
 ### Install instructions
 ### Install instructions
 - [node](https://nodejs.org/en/download/package-manager/) >=6.x and [yarn](https://yarnpkg.com/lang/en/docs/install/) are required
 - [node](https://nodejs.org/en/download/package-manager/) >=6.x and [yarn](https://yarnpkg.com/lang/en/docs/install/) are required
 - clone repo
 - clone repo
-- run `yarn install` to install packages and dependencies
-- rename `config.sample.js` to `config.js` in `src` directory
-- change the `coriolisUrl` variable to match the coriolis server path
+- run `yarn install` or `yarn install --production` to install packages and dependencies for development or production mode
+- change the `coriolisUrl` variable in ./src/config.js to match the Coriolis Server path
 
 
 
 
 ### Build instructions
 ### Build instructions
 - run `yarn build`
 - run `yarn build`
-- run `node build/server.js` to execute the build
+- run `node server.js` to start the server
 
 
 Your server will be running at http://localhost:3000/
 Your server will be running at http://localhost:3000/
 
 
@@ -23,7 +22,7 @@ Your development server will be running at http://localhost:3000/
 
 
 This should be used only for development, as it contains live-reload and other development tools.
 This should be used only for development, as it contains live-reload and other development tools.
 
 
-
+You can view some of the UIs components in the [Storybook](https://github.com/storybooks/storybook) by running `yarn storybook`
 
 
 ### Apache Configuration
 ### Apache Configuration
 
 
@@ -51,4 +50,4 @@ ProxyPassMatch ^/identity/(.*)$ http://127.0.0.1:5000/v3/$1
 ProxyPassMatch ^/barbican/(.*)$ http://127.0.0.1:9311/$1
 ProxyPassMatch ^/barbican/(.*)$ http://127.0.0.1:9311/$1
 ProxyPassMatch ^/coriolis/(.*)$ http://127.0.0.1:7667/v1/$1
 ProxyPassMatch ^/coriolis/(.*)$ http://127.0.0.1:7667/v1/$1
 ProxyPassMatch ^/((?!identity|coriolis|barbican).*)$ http://127.0.0.1:3000/$1
 ProxyPassMatch ^/((?!identity|coriolis|barbican).*)$ http://127.0.0.1:3000/$1
-```
+```

+ 84 - 129
package.json

@@ -1,137 +1,92 @@
 {
 {
-  "private": true,
-  "engines": {
-    "node": ">=5.0 <7",
-    "npm": ">=3.3 <4"
-  },
-  "dependencies": {
-    "babel-polyfill": "6.6.1",
-    "babel-preset-jest": "^20.0.3",
-    "babel-runtime": "6.6.1",
-    "bluebird": "3.3.3",
-    "body-parser": "1.15.0",
-    "classnames": "2.2.3",
-    "cookie-parser": "1.4.1",
-    "estraverse-fb": "^1.3.2",
-    "eventemitter3": "1.1.1",
-    "express": "4.13.4",
-    "express-jwt": "3.3.0",
-    "fastclick": "1.0.6",
-    "fbjs": "0.7.2",
-    "front-matter": "2.0.6",
-    "history": "2.0.0",
-    "immutability-helper": "2.1.2",
-    "isomorphic-style-loader": "0.0.10",
-    "jade": "1.11.0",
-    "jest": "^20.0.4",
-    "jsonwebtoken": "5.7.0",
-    "markdown-it": "6.0.0",
-    "moment": "2.17.1",
-    "moment-timezone": "0.5.11",
-    "node-fetch": "1.3.3",
-    "normalize.css": "3.0.3",
-    "passport": "0.3.2",
-    "passport-facebook": "2.1.0",
-    "pretty-error": "2.0.0",
-    "react": "15.4.2",
-    "react-addons-css-transition-group": "^15.5.2",
-    "react-collapse": "2.3.3",
-    "react-cookie": "^1.0.5",
-    "react-datepicker": "0.40.0",
-    "react-dom": "15.4.2",
-    "react-dropdown": "1.2.0",
-    "react-height": "2.2.0",
-    "react-modal": "1.6.5",
-    "react-moment": "0.1.2",
-    "react-motion": "0.4.7",
-    "react-notification-system": "^0.2.14",
-    "react-notifications": "^1.3.0",
-    "react-routing": "0.0.7",
-    "react-svg": "2.1.16",
-    "react-text-truncate": "0.8.3",
-    "react-tooltip": "3.2.7",
-    "reflux": "6.0.0",
-    "sinon": "^3.0.0",
-    "source-map-support": "0.4.0",
-    "svg-injector": "1.1.3",
-    "svg-react-loader": "0.3.7",
-    "whatwg-fetch": "0.11.0"
-  },
-  "devDependencies": {
-    "assets-webpack-plugin": "3.3.0",
-    "autoprefixer": "6.3.3",
-    "babel-cli": "6.6.0",
-    "babel-eslint": "5.0.0",
-    "babel-jest": "^20.0.3",
-    "babel-loader": "6.2.4",
-    "babel-plugin-react-transform": "2.0.0",
-    "babel-plugin-transform-runtime": "6.6.0",
-    "babel-preset-es2015": "6.6.0",
-    "babel-preset-react": "6.5.0",
-    "babel-preset-stage-0": "6.5.0",
-    "browser-sync": "2.11.1",
-    "core-js": "2.1.3",
-    "css-loader": "0.23.1",
-    "csscomb": "3.1.8",
-    "del": "2.2.0",
-    "enzyme": "^2.9.1",
-    "eslint": "2.3.0",
-    "eslint-config-airbnb": "6.0.2",
-    "eslint-loader": "1.3.0",
-    "eslint-plugin-react": "4.1.0",
-    "extend": "3.0.0",
-    "file-loader": "0.8.5",
-    "gaze": "0.5.2",
-    "git-repository": "0.1.1",
-    "glob": "7.0.0",
-    "jade-loader": "0.8.0",
-    "jest-cli": "21.2.1",
-    "jscs": "2.10.1",
-    "json-loader": "0.5.4",
-    "mkdirp": "0.5.1",
-    "ncp": "2.0.0",
-    "postcss": "5.0.18",
-    "postcss-import": "8.0.2",
-    "postcss-loader": "0.8.1",
-    "postcss-scss": "0.1.6",
-    "precss": "1.4.0",
-    "raw-loader": "0.5.1",
-    "react-addons-test-utils": "^15.6.0",
-    "react-modal": "1.6.5",
-    "react-svg": "2.1.16",
-    "react-test-renderer": "15.4.2",
-    "react-transform-catch-errors": "1.0.2",
-    "react-transform-hmr": "1.0.2",
-    "redbox-react": "1.2.2",
-    "replace": "0.3.0",
-    "style-loader": "0.13.1",
-    "url-loader": "0.5.7",
-    "webpack": "1.12.14",
-    "webpack-hot-middleware": "2.8.1",
-    "webpack-middleware": "1.4.0"
+  "name": "coriolis-web",
+  "version": "1.4.0",
+  "license": "AGPL-3.0",
+  "scripts": {
+    "start": "npm run env:dev && node server.js --dev",
+    "env:dev": "cross-env NODE_ENV=development",
+    "env:prod": "cross-env NODE_ENV=production",
+    "test": "jest --runInBand",
+    "storybook": "start-storybook -p 9001 -c private/storybook",
+    "lint": "eslint src private webpack.config.js --ext js,jsx",
+    "build:clean": "rimraf \"dist/!(.git*|Procfile)**\"",
+    "build:copy": "copyfiles -u 1 public/* public/**/* dist",
+    "prebuild": "npm run build:clean && npm run build:copy",
+    "build": "npm run env:prod -- webpack"
   },
   },
   "jest": {
   "jest": {
-    "rootDir": "./src",
-    "transform": {
-      ".*": "../preprocessor.js"
+    "verbose": true,
+    "moduleDirectories": [
+      "src",
+      "node_modules"
+    ],
+    "moduleNameMapper": {
+      "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/private/jest/fileMock.js",
+      "^components$": "<rootDir>/private/jest/componentsMock.js"
     },
     },
-    "unmockedModulePathPatterns": [
-      "fbjs",
-      "react",
-      "parse-sdk",
-      "parse"
+    "setupFiles": [
+      "<rootDir>/private/jest/shim.js",
+      "<rootDir>/private/jest/setupTests.js"
     ]
     ]
   },
   },
-  "scripts": {
-    "lint": "eslint src tools",
-    "csslint": "csscomb src/components --lint --verbose",
-    "csscomb": "csscomb src/components --verbose",
-    "test": "eslint src && jest",
-    "clean": "babel-node tools/run clean",
-    "copy": "babel-node tools/run copy",
-    "bundle": "babel-node tools/run bundle",
-    "build": "babel-node tools/run build",
-    "deploy": "babel-node tools/run deploy",
-    "start": "babel-node tools/run start"
+  "devDependencies": {
+    "@storybook/react": "^3.2.15",
+    "babel-eslint": "^8.0.1",
+    "babel-jest": "^21.2.0",
+    "enzyme": "^3.1.0",
+    "enzyme-adapter-react-16": "^1.0.4",
+    "eslint": "^4.8.0",
+    "eslint-config-airbnb": "^15.1.0",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-jsx-a11y": "^6.0.2",
+    "eslint-plugin-react": "^7.4.0",
+    "jest": "^21.2.1",
+    "react-test-renderer": "^16.0.0",
+    "sinon": "^4.1.2",
+    "webpack-dev-middleware": "^1.12.0",
+    "webpack-hot-middleware": "^2.19.1"
+  },
+  "dependencies": {
+    "@webpack-blocks/webpack2": "^0.4.0",
+    "alt": "^0.18.6",
+    "alt-utils": "^2.0.0",
+    "babel-core": "^6.26.0",
+    "babel-loader": "^7.1.2",
+    "babel-plugin-styled-components": "^1.2.1",
+    "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
+    "babel-plugin-transform-react-remove-prop-types": "^0.4.9",
+    "babel-preset-env": "^1.6.0",
+    "babel-preset-react": "^6.24.1",
+    "babel-preset-stage-1": "^6.24.1",
+    "copyfiles": "^1.2.0",
+    "cross-env": "^5.0.5",
+    "express": "^4.16.1",
+    "file-loader": "^1.1.5",
+    "fs": "^0.0.1-security",
+    "history": "^4.7.2",
+    "html-webpack-plugin": "^2.30.1",
+    "js-cookie": "^2.1.4",
+    "path": "^0.12.7",
+    "raw-loader": "^0.5.1",
+    "lodash": "^4.17.4",
+    "moment": "^2.18.1",
+    "prop-types": "^15.6.0",
+    "react": "^16.0.0",
+    "react-collapse": "^4.0.3",
+    "react-datetime": "^2.10.3",
+    "react-dom": "^16.0.0",
+    "react-hot-loader": "next",
+    "react-lines-ellipsis": "^0.8.0",
+    "react-modal": "^3.0.4",
+    "react-motion": "^0.5.2",
+    "react-notification-system": "^0.2.15",
+    "react-router-dom": "^4.2.2",
+    "rimraf": "^2.6.2",
+    "styled-components": "2.2.0",
+    "styled-tools": "^0.2.2",
+    "url-loader": "^0.6.2",
+    "webpack": "^3.6.0",
+    "webpack-blocks-happypack": "^0.1.3",
+    "webpack-blocks-split-vendor": "^0.2.1"
   }
   }
 }
 }

+ 0 - 26
preprocessor.js

@@ -1,26 +0,0 @@
-/**
- * React Starter Kit (https://www.reactstarterkit.com/)
- *
- * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-'use strict';
-
-var babel = require('babel-core');
-
-module.exports = {
-  process: function(src, filename) {
-    // Ignore files other than .js, .es, .jsx or .es6
-    if (!babel.util.canCompile(filename)) {
-      return '';
-    }
-    // Ignore all files within node_modules
-    if (filename.indexOf('node_modules') === -1) {
-      return babel.transform(src, {filename: filename}).code;
-    }
-    return src;
-  }
-};

+ 16 - 0
private/jest/componentsMock.js

@@ -0,0 +1,16 @@
+// https://github.com/diegohaz/arc/wiki/Testing-components
+import React from 'react'
+import PropTypes from 'prop-types'
+
+module.exports = new Proxy({}, {
+  get: (target, property) => {
+    const Mock = props => React.createElement('span', null, props.children)
+
+    Mock.displayName = property
+    Mock.propTypes = {
+      children: PropTypes.any,
+    }
+
+    return Mock
+  },
+})

+ 1 - 0
private/jest/fileMock.js

@@ -0,0 +1 @@
+export default 'file'

+ 4 - 0
private/jest/setupTests.js

@@ -0,0 +1,4 @@
+import { configure } from 'enzyme'
+import Adapter from 'enzyme-adapter-react-16'
+
+configure({ adapter: new Adapter() })

+ 3 - 0
private/jest/shim.js

@@ -0,0 +1,3 @@
+global.requestAnimationFrame = /* istanbul ignore next */ (callback) => {
+  setTimeout(callback, 0)
+}

+ 31 - 0
private/storybook/Decorator.jsx

@@ -0,0 +1,31 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import React from 'react'
+import styled from 'styled-components'
+import PropTypes from 'prop-types'
+import Palette from '../../src/components/styleUtils/Palette'
+
+const Wrapper = styled.div`
+  background: ${Palette.grayscale[7]};
+  padding: 32px;
+`
+
+const Decorator = ({ children }) => <Wrapper>{children}</Wrapper>
+
+Decorator.propTypes = {
+  children: PropTypes.node,
+}
+
+export default Decorator

+ 18 - 0
private/storybook/config.js

@@ -0,0 +1,18 @@
+import React from 'react'
+import { configure, addDecorator } from '@storybook/react'
+import { BrowserRouter } from 'react-router-dom'
+import Decorator from './Decorator'
+
+const req = require.context('components', true, /story.jsx$/i)
+
+function loadStories() {
+  req.keys().forEach(filename => req(filename))
+}
+
+addDecorator(story => {
+  return React.createElement(BrowserRouter, null,
+    React.createElement(Decorator, null, story())
+  )
+})
+
+configure(loadStories, module)

+ 11 - 0
private/storybook/webpack.config.js

@@ -0,0 +1,11 @@
+const baseConfig = require('../../webpack.config')
+
+module.exports = storybookBaseConfig =>
+  Object.assign({}, storybookBaseConfig, {
+    resolve: Object.assign({}, storybookBaseConfig.resolve, {
+      modules: baseConfig.resolve.modules,
+    }),
+    module: Object.assign({}, storybookBaseConfig.module, {
+      rules: storybookBaseConfig.module.rules.concat(baseConfig.module.rules.slice(1)),
+    }),
+  })

BIN
public/android-chrome-192x192.png


BIN
public/android-chrome-256x256.png


BIN
public/apple-touch-icon.png


+ 9 - 0
public/browserconfig.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<browserconfig>
+    <msapplication>
+        <tile>
+            <square150x150logo src="/mstile-150x150.png"/>
+            <TileColor>#da532c</TileColor>
+        </tile>
+    </msapplication>
+</browserconfig>

BIN
public/favicon-16x16.png


BIN
public/favicon-32x32.png


BIN
public/favicon.ico


+ 20 - 0
public/index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Coriolis</title>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
+  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
+  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
+  <link rel="manifest" href="/manifest.json">
+  <meta name="theme-color" content="#ffffff">
+  <script src="env.js"></script>
+</head>
+
+<body>
+  <main id="app"></main>
+</body>
+
+</html>

+ 18 - 0
public/manifest.json

@@ -0,0 +1,18 @@
+{
+    "name": "",
+    "icons": [
+        {
+            "src": "/android-chrome-192x192.png",
+            "sizes": "192x192",
+            "type": "image/png"
+        },
+        {
+            "src": "/android-chrome-256x256.png",
+            "sizes": "256x256",
+            "type": "image/png"
+        }
+    ],
+    "theme_color": "#ffffff",
+    "background_color": "#ffffff",
+    "display": "standalone"
+}

BIN
public/mstile-150x150.png


+ 77 - 0
server.js

@@ -0,0 +1,77 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+var express = require('express');
+var fs = require('fs')
+
+// Create our app
+var app = express();
+var PORT = process.env.PORT || 3000;
+
+// Write file to disk with process env variables, so that the client code can read
+if (!fs.existsSync('./dist')) {
+  fs.mkdirSync('./dist');
+}
+fs.writeFileSync('./dist/env.js', 'window.env = { CORIOLIS_URL: "' + (process.env.CORIOLIS_URL || '/') + '" }')
+
+let isDev = process.argv.find(a => a === '--dev')
+if (isDev) {
+  let isBrowserOpen = false
+  var webpack = require('webpack');
+  var webpackConfig = require('./webpack.config');
+  var compiler = webpack(webpackConfig);
+
+  app.use(require("webpack-dev-middleware")(compiler, {
+    noInfo: false,
+    publicPath: webpackConfig.output.publicPath,
+    stats: {
+      colors: true
+    },
+    log: function (text) {
+      let statusIndex = text.indexOf('webpack: Compiled') > -1
+        ? text.indexOf('webpack: Compiled') : text.indexOf('webpack: Failed')
+      if (statusIndex > -1) {
+        let left = text.substr(0, statusIndex)
+        let isSuccesfull = text.indexOf('webpack: Compiled successfully.') > -1
+        let color = text.indexOf('webpack: Compiled with warnings.') > -1 ? '\033[43m\033[30m' : ''
+        color = isSuccesfull ? '\033[42m\033[30m' : color
+        color = text.indexOf('webpack: Failed to compile.') > -1 ? '\033[41m' : color
+
+        let end = color + text.substr(statusIndex) + '\033[0m'
+        console.log(left + end)
+
+        if (!isBrowserOpen && isSuccesfull) {
+          isBrowserOpen = true
+          console.log('\033[96mServer is available at http://localhost:' + PORT + '\033[0m')
+        }
+      } else {
+        console.log(text)
+      }
+    } 
+  }));
+
+  app.use(require("webpack-hot-middleware")(compiler, {
+    log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000
+  }));
+}
+
+app.use(express.static('dist'));
+
+app.use(function (req, res, next) {
+  res.redirect(req.baseUrl + '/#' + req.url)
+});
+
+app.listen(PORT, function () {
+  console.log('Express server is up on port ' + PORT);
+});

+ 0 - 33
src/__tests__/LoadingIcon.test.js

@@ -1,33 +0,0 @@
-import React from 'react';
-import { LoadingIcon } from '../components/LoadingIcon';
-import renderer from 'react-test-renderer';
-
-describe('Loading icon', () => {
-  it('renders correctly', () => {
-    const tree = renderer.create(
-      <LoadingIcon />
-    ).toJSON();
-    expect(tree).toMatchSnapshot();
-  });
-
-  it('renders correctly animated', () => {
-    const tree = renderer.create(
-      <LoadingIcon animate />
-    ).toJSON();
-    expect(tree).toMatchSnapshot();
-  });
-
-  it('renders correctly with text', () => {
-    const tree = renderer.create(
-      <LoadingIcon text="Test text" />
-    ).toJSON();
-    expect(tree).toMatchSnapshot();
-  });
-
-  it('renders correctly with sizes', () => {
-    const tree = renderer.create(
-      <LoadingIcon width={300} height={200} />
-    ).toJSON();
-    expect(tree).toMatchSnapshot();
-  });
-})

+ 0 - 32
src/__tests__/LoginPage.test.js

@@ -1,32 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import App from '../components/App'
-import { LoginPage } from '../components/LoginPage';
-import renderer from 'react-test-renderer';
-import sinon from 'sinon';
-
-
-describe('Login Page', () => {
-  const tree = renderer.create(
-    <App context={{ insertCss: () => {} }}>
-      <LoginPage />
-    </App>
-  ).toJSON();
-
-  it('renders correctly', () => {
-    expect(tree).toMatchSnapshot();
-  });
-
-  it('simulate click event', () => {
-    const onBtnClick = sinon.spy()
-    const loginForm = shallow(
-      <App context={{ insertCss: () => {} }}>
-        <LoginPage />
-      </App>
-    )
-
-    loginForm.find('button').simulate('click');
-
-    expect(onBtnClick).to.have.property('callCount', 1);
-  })
-})

+ 0 - 19
src/__tests__/ProgressBar.test.js

@@ -1,19 +0,0 @@
-import React from 'react';
-import { ProgressBar } from '../components/ProgressBar';
-import renderer from 'react-test-renderer';
-
-describe('Progress Bar', () => {
-  it('renders correctly', () => {
-    const tree = renderer.create(
-      <ProgressBar />
-    ).toJSON();
-    expect(tree).toMatchSnapshot();
-  });
-
-  it('renders progress correctly', () => {
-    const tree = renderer.create(
-      <ProgressBar progress={20} />
-    ).toJSON();
-    expect(tree).toMatchSnapshot();
-  });
-})

+ 0 - 209
src/__tests__/__snapshots__/LoadingIcon.test.js.snap

@@ -1,209 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Loading icon renders correctly 1`] = `
-<div
-  className={undefined}
-  style={
-    Object {
-      "paddingBottom": 32,
-      "paddingTop": 32,
-    }
-  }
->
-  <div
-    className={false}
-    dangerouslySetInnerHTML={
-      Object {
-        "__html": "
-      <svg id=\\"loadingIcon\\" width=\\"137px\\" height=\\"122px\\" viewBox=\\"0 0 137 122\\" version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\">
-          <g id=\\"Symbols\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\">
-              <g id=\\"Coriolis_Symbol_Large\\">
-                  <g id=\\"Group-3\\">
-                      <path d=\\"M95.8871,1.2248 C95.6601,1.2248 95.4321,1.2268 95.2021,1.2318 C78.3771,1.5528 57.5671,13.3988 40.8941,32.1488 C15.2921,60.9408 6.7641,97.1778 21.8841,112.9268 C26.5931,117.8328 33.3131,120.3458 41.3081,120.1918 C58.1341,119.8708 78.9431,108.0238 95.6161,89.2748 C121.2181,60.4828 129.7471,24.2458 114.6271,8.4968 C110.0521,3.7318 103.5841,1.2248 95.8871,1.2248 M40.6221,121.3128 C32.6151,121.3128 25.8701,118.6878 21.0801,113.6988 C5.5671,97.5398 14.0821,60.6248 40.0621,31.4078 C56.9341,12.4338 78.0551,0.4438 95.1811,0.1178 C103.5041,-0.0442 110.5001,2.5888 115.4311,7.7258 C130.9441,23.8838 122.4281,60.7988 96.4481,90.0148 C79.5761,108.9888 58.4561,120.9798 41.3301,121.3068 C41.0921,121.3108 40.8561,121.3128 40.6221,121.3128\\" id=\\"Fill-1\\" fill=\\"#E62565\\"></path>
-                      <path d=\\"M17.7337,102.4263 C21.3347,106.9853 26.9857,109.7933 34.0757,110.5453 C50.0377,112.2403 71.4927,103.7513 90.0747,88.3913 C117.9157,65.3793 130.7487,34.2303 118.6817,18.9553 C115.0807,14.3963 109.4297,11.5883 102.3397,10.8363 C86.3827,9.1433 64.9227,17.6313 46.3417,32.9893 C18.5007,56.0013 5.6667,87.1503 17.7337,102.4263 M38.2057,111.8713 C36.7527,111.8713 35.3357,111.7993 33.9577,111.6533 C26.5627,110.8683 20.6507,107.9163 16.8587,103.1163 C4.4207,87.3693 17.3277,55.5253 45.6317,32.1303 C64.4357,16.5883 86.2117,8.0043 102.4567,9.7283 C109.8517,10.5133 115.7647,13.4653 119.5567,18.2643 C131.9957,34.0113 119.0887,65.8553 90.7847,89.2513 C73.5747,103.4753 53.8767,111.8713 38.2057,111.8713\\" id=\\"Fill-2\\" fill=\\"#FF2D55\\"></path>
-                      <path d=\\"M98.9136,19.4672 C85.3926,19.4672 68.3216,24.3372 51.7716,33.8262 C30.9866,45.7422 15.7566,62.1752 12.0246,76.7122 C10.5056,82.6272 11.0316,87.7502 13.5866,91.9402 C22.6216,106.7532 54.4546,104.7682 84.5496,87.5142 C105.3346,75.5982 120.5646,59.1652 124.2966,44.6272 C125.8146,38.7132 125.2896,33.5902 122.7346,29.3992 C118.6676,22.7322 109.9786,19.4672 98.9136,19.4672 M37.4626,102.9262 C25.9576,102.9262 16.8936,99.5022 12.6356,92.5202 C9.9176,88.0642 9.3486,82.6522 10.9456,76.4352 C14.7496,61.6172 30.1806,44.9192 51.2176,32.8592 C81.8306,15.3072 114.3406,13.4952 123.6856,28.8192 C126.4036,33.2762 126.9716,38.6882 125.3756,44.9042 C121.5716,59.7232 106.1406,76.4202 85.1036,88.4812 C68.4386,98.0352 51.2086,102.9262 37.4626,102.9262\\" id=\\"Fill-3\\" fill=\\"#FC5830\\"></path>
-                      <path d=\\"M9.4403,81.4743 C15.4543,95.8313 46.6743,98.1533 79.0373,86.6483 C101.9923,78.4883 120.4523,65.0103 126.0673,52.3123 C128.0943,47.7273 128.3363,43.5253 126.7863,39.8243 C120.7703,25.4663 89.5513,23.1453 57.1893,34.6493 C34.2343,42.8093 15.7733,56.2873 10.1593,68.9863 C8.1323,73.5713 7.8903,77.7733 9.4403,81.4743 M40.1333,95.0673 C38.0613,95.0673 36.0383,94.9913 34.0753,94.8383 C20.5793,93.7853 11.4653,89.1923 8.4133,81.9043 C6.7393,77.9103 6.9843,73.4123 9.1403,68.5353 C14.8723,55.5713 33.5863,41.8573 56.8163,33.5993 C72.6973,27.9533 88.7963,25.4183 102.1513,26.4603 C115.6473,27.5133 124.7613,32.1063 127.8133,39.3943 C129.4873,43.3883 129.2423,47.8863 127.0863,52.7633 C121.3543,65.7273 102.6403,79.4403 79.4103,87.6983 C65.8643,92.5143 52.1583,95.0673 40.1333,95.0673\\" id=\\"Fill-4\\" fill=\\"#FD9727\\"></path>
-                      <path d=\\"M5.2865,71.0308 C8.2875,84.9278 38.9025,91.5548 73.5305,85.8008 C99.2035,81.5358 120.7115,71.5368 128.3265,60.3278 C130.6935,56.8438 131.5405,53.4458 130.8455,50.2258 C127.8445,36.3288 97.2315,29.7038 62.6025,35.4558 C36.9295,39.7208 15.4205,49.7188 7.8045,60.9288 C5.4385,64.4118 4.5915,67.8108 5.2865,71.0308 M49.2235,88.9908 C41.2215,88.9908 33.7145,88.2598 27.0975,86.7998 C13.8625,83.8788 5.7295,78.3618 4.1975,71.2658 C3.4355,67.7368 4.3385,64.0478 6.8835,60.3028 C14.6615,48.8538 36.4605,38.6688 62.4195,34.3568 C79.4025,31.5348 95.9575,31.5708 109.0355,34.4568 C122.2695,37.3778 130.4015,42.8948 131.9345,49.9908 C132.6965,53.5198 131.7935,57.2078 129.2485,60.9538 C121.4705,72.4028 99.6715,82.5878 73.7125,86.8998 C65.3225,88.2938 57.0375,88.9908 49.2235,88.9908\\" id=\\"Fill-5\\" fill=\\"#FDC02F\\"></path>
-                      <path d=\\"M68.0188,36.4536 C31.1278,36.4536 1.1138,47.3826 1.1138,60.8156 C1.1138,74.2496 31.1278,85.1786 68.0188,85.1786 C104.9098,85.1786 134.9228,74.2496 134.9228,60.8156 C134.9228,47.3826 104.9098,36.4536 68.0188,36.4536 M68.0188,86.2926 C49.9348,86.2926 32.9248,83.6876 20.1228,78.9596 C7.1468,74.1656 -0.0002,67.7226 -0.0002,60.8156 C-0.0002,53.9096 7.1468,47.4656 20.1228,42.6726 C32.9248,37.9436 49.9348,35.3396 68.0188,35.3396 C86.1028,35.3396 103.1118,37.9436 115.9138,42.6726 C128.8908,47.4656 136.0368,53.9096 136.0368,60.8156 C136.0368,67.7226 128.8908,74.1656 115.9138,78.9596 C103.1128,83.6876 86.1028,86.2926 68.0188,86.2926\\" id=\\"Fill-6\\" fill=\\"#FFDC00\\"></path>
-                      <path d=\\"M6.35082759,49.9596832 C5.65582759,53.1796832 6.50282759,56.5786832 8.86882759,60.0616832 C16.4848276,71.2716832 37.9938276,81.2696832 63.6668276,85.5346832 C72.1398276,86.9426832 80.3768276,87.6086832 88.0238276,87.6096832 C111.627828,87.6096832 129.642828,81.2616832 131.909828,70.7646832 C132.604828,67.5446832 131.757828,64.1466832 129.390828,60.6636832 C121.776828,49.4536832 100.267828,39.4546832 74.5948276,35.1896832 C39.9688276,29.4386832 9.35182759,36.0626832 6.35082759,49.9596832 M87.9738276,88.7246832 C80.1598276,88.7246832 71.8748276,88.0276832 63.4838276,86.6336832 C37.5248276,82.3216832 15.7258276,72.1376832 7.94782759,60.6876832 C5.40282759,56.9426832 4.49982759,53.2536832 5.26182759,49.7246832 C6.79382759,42.6286832 14.9268276,37.1126832 28.1618276,34.1916832 C41.2388276,31.3046832 57.7938276,31.2686832 74.7768276,34.0906832 C100.735828,38.4036832 122.534828,48.5876832 130.313828,60.0366832 C132.857828,63.7826832 133.760828,67.4706832 132.998828,70.9996832 C131.465828,78.0956832 123.333828,83.6126832 110.099828,86.5336832 C103.482828,87.9936832 95.9758276,88.7246832 87.9738276,88.7246832\\" id=\\"Fill-7\\" fill=\\"#CDDA49\\"></path>
-                      <path d=\\"M9.4403,39.9491 C7.8903,43.6501 8.1323,47.8521 10.1593,52.4371 C15.7733,65.1361 34.2343,78.6141 57.1893,86.7741 C89.5503,98.2791 120.7713,95.9561 126.7863,81.5991 C128.3363,77.8981 128.0943,73.6961 126.0673,69.1111 C120.4523,56.4121 101.9923,42.9341 79.0373,34.7741 C46.6763,23.2701 15.4563,25.5911 9.4403,39.9491 M96.0923,95.1921 C84.0673,95.1921 70.3623,92.6401 56.8163,87.8241 C33.5863,79.5661 14.8723,65.8521 9.1403,52.8881 C6.9843,48.0111 6.7393,43.5131 8.4133,39.5191 C11.4653,32.2311 20.5793,27.6381 34.0753,26.5851 C47.4313,25.5421 63.5293,28.0781 79.4103,33.7241 C102.6403,41.9821 121.3543,55.6951 127.0863,68.6601 C129.2423,73.5371 129.4873,78.0351 127.8133,82.0291 C124.7613,89.3171 115.6473,93.9101 102.1513,94.9631 C100.1873,95.1161 98.1653,95.1921 96.0923,95.1921\\" id=\\"Fill-8\\" fill=\\"#8CC152\\"></path>
-                      <path d=\\"M37.4075,19.5507 C26.3395,19.5507 17.6535,22.8147 13.5865,29.4827 C11.0315,33.6727 10.5065,38.7967 12.0245,44.7107 C15.7565,59.2487 30.9865,75.6817 51.7715,87.5977 C81.8655,104.8517 113.6995,106.8377 122.7345,92.0237 C125.2895,87.8337 125.8155,82.7107 124.2965,76.7967 C120.5645,62.2587 105.3345,45.8257 84.5495,33.9087 C68.0015,24.4217 50.9275,19.5507 37.4075,19.5507 M98.8585,103.0097 C85.1115,103.0097 67.8845,98.1197 51.2175,88.5647 C30.1805,76.5037 14.7495,59.8067 10.9455,44.9877 C9.3485,38.7717 9.9175,33.3597 12.6355,28.9027 C21.9805,13.5797 54.4905,15.3907 85.1035,32.9427 C106.1405,45.0027 121.5715,61.7007 125.3755,76.5187 C126.9715,82.7357 126.4035,88.1477 123.6855,92.6037 C119.4275,99.5847 110.3615,103.0097 98.8585,103.0097\\" id=\\"Fill-9\\" fill=\\"#159588\\"></path>
-                      <path d=\\"M17.2963,18.6519 L17.7333,18.9969 C5.6663,34.2729 18.5003,65.4219 46.3413,88.4339 C64.9233,103.7929 86.3753,112.2799 102.3403,110.5869 C109.4293,109.8349 115.0803,107.0269 118.6823,102.4679 C130.7493,87.1929 117.9153,56.0439 90.0753,33.0319 C71.4923,17.6719 50.0303,9.1809 34.0753,10.8779 C26.9853,11.6309 21.3343,14.4379 17.7333,18.9969 L17.2963,18.6519 Z M98.2093,111.9139 C82.5383,111.9139 62.8413,103.5179 45.6323,89.2929 C17.3273,65.8979 4.4203,34.0539 16.8593,18.3069 C20.6513,13.5069 26.5633,10.5549 33.9573,9.7699 C50.2033,8.0459 71.9803,16.6299 90.7843,32.1729 C119.0883,55.5679 131.9953,87.4119 119.5573,103.1579 C115.7653,107.9589 109.8523,110.9109 102.4573,111.6949 C101.0803,111.8409 99.6613,111.9139 98.2093,111.9139 L98.2093,111.9139 Z\\" id=\\"Fill-10\\" fill=\\"#2F81B7\\"></path>
-                      <path d=\\"M40.6234,1.2244 C32.9264,1.2244 26.4584,3.7314 21.8834,8.4964 C6.7634,24.2454 15.2924,60.4824 40.8944,89.2744 C57.5674,108.0244 78.3774,119.8714 95.2024,120.1924 C103.2004,120.3364 109.9174,117.8324 114.6264,112.9264 C129.7464,97.1774 121.2184,60.9404 95.6154,32.1484 C78.9434,13.3994 58.1344,1.5524 41.3084,1.2314 C41.0784,1.2264 40.8504,1.2244 40.6234,1.2244 M95.8884,121.3124 C95.6534,121.3124 95.4184,121.3104 95.1804,121.3064 C78.0544,120.9794 56.9344,108.9894 40.0624,90.0154 C14.0824,60.7984 5.5664,23.8834 21.0794,7.7254 C26.0104,2.5894 33.0144,-0.0516 41.3294,0.1174 C58.4554,0.4444 79.5764,12.4344 96.4484,31.4084 C122.4284,60.6244 130.9444,97.5394 115.4304,113.6984 C110.6404,118.6874 103.8944,121.3124 95.8884,121.3124\\" id=\\"Fill-11\\" fill=\\"#0056B8\\"></path>
-                  </g>
-              </g>
-          </g>
-      </svg>",
-      }
-    }
-    style={
-      Object {
-        "height": 122,
-        "margin": "0 auto",
-        "width": 137,
-      }
-    }
-  />
-  <div
-    className={undefined}
-  >
-    Loading...
-  </div>
-</div>
-`;
-
-exports[`Loading icon renders correctly animated 1`] = `
-<div
-  className={undefined}
-  style={
-    Object {
-      "paddingBottom": 32,
-      "paddingTop": 32,
-    }
-  }
->
-  <div
-    className={false}
-    dangerouslySetInnerHTML={
-      Object {
-        "__html": "
-      <svg id=\\"loadingIcon\\" width=\\"137px\\" height=\\"122px\\" viewBox=\\"0 0 137 122\\" version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\">
-          <g id=\\"Symbols\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\">
-              <g id=\\"Coriolis_Symbol_Large\\">
-                  <g id=\\"Group-3\\">
-                      <path d=\\"M95.8871,1.2248 C95.6601,1.2248 95.4321,1.2268 95.2021,1.2318 C78.3771,1.5528 57.5671,13.3988 40.8941,32.1488 C15.2921,60.9408 6.7641,97.1778 21.8841,112.9268 C26.5931,117.8328 33.3131,120.3458 41.3081,120.1918 C58.1341,119.8708 78.9431,108.0238 95.6161,89.2748 C121.2181,60.4828 129.7471,24.2458 114.6271,8.4968 C110.0521,3.7318 103.5841,1.2248 95.8871,1.2248 M40.6221,121.3128 C32.6151,121.3128 25.8701,118.6878 21.0801,113.6988 C5.5671,97.5398 14.0821,60.6248 40.0621,31.4078 C56.9341,12.4338 78.0551,0.4438 95.1811,0.1178 C103.5041,-0.0442 110.5001,2.5888 115.4311,7.7258 C130.9441,23.8838 122.4281,60.7988 96.4481,90.0148 C79.5761,108.9888 58.4561,120.9798 41.3301,121.3068 C41.0921,121.3108 40.8561,121.3128 40.6221,121.3128\\" id=\\"Fill-1\\" fill=\\"#E62565\\"></path>
-                      <path d=\\"M17.7337,102.4263 C21.3347,106.9853 26.9857,109.7933 34.0757,110.5453 C50.0377,112.2403 71.4927,103.7513 90.0747,88.3913 C117.9157,65.3793 130.7487,34.2303 118.6817,18.9553 C115.0807,14.3963 109.4297,11.5883 102.3397,10.8363 C86.3827,9.1433 64.9227,17.6313 46.3417,32.9893 C18.5007,56.0013 5.6667,87.1503 17.7337,102.4263 M38.2057,111.8713 C36.7527,111.8713 35.3357,111.7993 33.9577,111.6533 C26.5627,110.8683 20.6507,107.9163 16.8587,103.1163 C4.4207,87.3693 17.3277,55.5253 45.6317,32.1303 C64.4357,16.5883 86.2117,8.0043 102.4567,9.7283 C109.8517,10.5133 115.7647,13.4653 119.5567,18.2643 C131.9957,34.0113 119.0887,65.8553 90.7847,89.2513 C73.5747,103.4753 53.8767,111.8713 38.2057,111.8713\\" id=\\"Fill-2\\" fill=\\"#FF2D55\\"></path>
-                      <path d=\\"M98.9136,19.4672 C85.3926,19.4672 68.3216,24.3372 51.7716,33.8262 C30.9866,45.7422 15.7566,62.1752 12.0246,76.7122 C10.5056,82.6272 11.0316,87.7502 13.5866,91.9402 C22.6216,106.7532 54.4546,104.7682 84.5496,87.5142 C105.3346,75.5982 120.5646,59.1652 124.2966,44.6272 C125.8146,38.7132 125.2896,33.5902 122.7346,29.3992 C118.6676,22.7322 109.9786,19.4672 98.9136,19.4672 M37.4626,102.9262 C25.9576,102.9262 16.8936,99.5022 12.6356,92.5202 C9.9176,88.0642 9.3486,82.6522 10.9456,76.4352 C14.7496,61.6172 30.1806,44.9192 51.2176,32.8592 C81.8306,15.3072 114.3406,13.4952 123.6856,28.8192 C126.4036,33.2762 126.9716,38.6882 125.3756,44.9042 C121.5716,59.7232 106.1406,76.4202 85.1036,88.4812 C68.4386,98.0352 51.2086,102.9262 37.4626,102.9262\\" id=\\"Fill-3\\" fill=\\"#FC5830\\"></path>
-                      <path d=\\"M9.4403,81.4743 C15.4543,95.8313 46.6743,98.1533 79.0373,86.6483 C101.9923,78.4883 120.4523,65.0103 126.0673,52.3123 C128.0943,47.7273 128.3363,43.5253 126.7863,39.8243 C120.7703,25.4663 89.5513,23.1453 57.1893,34.6493 C34.2343,42.8093 15.7733,56.2873 10.1593,68.9863 C8.1323,73.5713 7.8903,77.7733 9.4403,81.4743 M40.1333,95.0673 C38.0613,95.0673 36.0383,94.9913 34.0753,94.8383 C20.5793,93.7853 11.4653,89.1923 8.4133,81.9043 C6.7393,77.9103 6.9843,73.4123 9.1403,68.5353 C14.8723,55.5713 33.5863,41.8573 56.8163,33.5993 C72.6973,27.9533 88.7963,25.4183 102.1513,26.4603 C115.6473,27.5133 124.7613,32.1063 127.8133,39.3943 C129.4873,43.3883 129.2423,47.8863 127.0863,52.7633 C121.3543,65.7273 102.6403,79.4403 79.4103,87.6983 C65.8643,92.5143 52.1583,95.0673 40.1333,95.0673\\" id=\\"Fill-4\\" fill=\\"#FD9727\\"></path>
-                      <path d=\\"M5.2865,71.0308 C8.2875,84.9278 38.9025,91.5548 73.5305,85.8008 C99.2035,81.5358 120.7115,71.5368 128.3265,60.3278 C130.6935,56.8438 131.5405,53.4458 130.8455,50.2258 C127.8445,36.3288 97.2315,29.7038 62.6025,35.4558 C36.9295,39.7208 15.4205,49.7188 7.8045,60.9288 C5.4385,64.4118 4.5915,67.8108 5.2865,71.0308 M49.2235,88.9908 C41.2215,88.9908 33.7145,88.2598 27.0975,86.7998 C13.8625,83.8788 5.7295,78.3618 4.1975,71.2658 C3.4355,67.7368 4.3385,64.0478 6.8835,60.3028 C14.6615,48.8538 36.4605,38.6688 62.4195,34.3568 C79.4025,31.5348 95.9575,31.5708 109.0355,34.4568 C122.2695,37.3778 130.4015,42.8948 131.9345,49.9908 C132.6965,53.5198 131.7935,57.2078 129.2485,60.9538 C121.4705,72.4028 99.6715,82.5878 73.7125,86.8998 C65.3225,88.2938 57.0375,88.9908 49.2235,88.9908\\" id=\\"Fill-5\\" fill=\\"#FDC02F\\"></path>
-                      <path d=\\"M68.0188,36.4536 C31.1278,36.4536 1.1138,47.3826 1.1138,60.8156 C1.1138,74.2496 31.1278,85.1786 68.0188,85.1786 C104.9098,85.1786 134.9228,74.2496 134.9228,60.8156 C134.9228,47.3826 104.9098,36.4536 68.0188,36.4536 M68.0188,86.2926 C49.9348,86.2926 32.9248,83.6876 20.1228,78.9596 C7.1468,74.1656 -0.0002,67.7226 -0.0002,60.8156 C-0.0002,53.9096 7.1468,47.4656 20.1228,42.6726 C32.9248,37.9436 49.9348,35.3396 68.0188,35.3396 C86.1028,35.3396 103.1118,37.9436 115.9138,42.6726 C128.8908,47.4656 136.0368,53.9096 136.0368,60.8156 C136.0368,67.7226 128.8908,74.1656 115.9138,78.9596 C103.1128,83.6876 86.1028,86.2926 68.0188,86.2926\\" id=\\"Fill-6\\" fill=\\"#FFDC00\\"></path>
-                      <path d=\\"M6.35082759,49.9596832 C5.65582759,53.1796832 6.50282759,56.5786832 8.86882759,60.0616832 C16.4848276,71.2716832 37.9938276,81.2696832 63.6668276,85.5346832 C72.1398276,86.9426832 80.3768276,87.6086832 88.0238276,87.6096832 C111.627828,87.6096832 129.642828,81.2616832 131.909828,70.7646832 C132.604828,67.5446832 131.757828,64.1466832 129.390828,60.6636832 C121.776828,49.4536832 100.267828,39.4546832 74.5948276,35.1896832 C39.9688276,29.4386832 9.35182759,36.0626832 6.35082759,49.9596832 M87.9738276,88.7246832 C80.1598276,88.7246832 71.8748276,88.0276832 63.4838276,86.6336832 C37.5248276,82.3216832 15.7258276,72.1376832 7.94782759,60.6876832 C5.40282759,56.9426832 4.49982759,53.2536832 5.26182759,49.7246832 C6.79382759,42.6286832 14.9268276,37.1126832 28.1618276,34.1916832 C41.2388276,31.3046832 57.7938276,31.2686832 74.7768276,34.0906832 C100.735828,38.4036832 122.534828,48.5876832 130.313828,60.0366832 C132.857828,63.7826832 133.760828,67.4706832 132.998828,70.9996832 C131.465828,78.0956832 123.333828,83.6126832 110.099828,86.5336832 C103.482828,87.9936832 95.9758276,88.7246832 87.9738276,88.7246832\\" id=\\"Fill-7\\" fill=\\"#CDDA49\\"></path>
-                      <path d=\\"M9.4403,39.9491 C7.8903,43.6501 8.1323,47.8521 10.1593,52.4371 C15.7733,65.1361 34.2343,78.6141 57.1893,86.7741 C89.5503,98.2791 120.7713,95.9561 126.7863,81.5991 C128.3363,77.8981 128.0943,73.6961 126.0673,69.1111 C120.4523,56.4121 101.9923,42.9341 79.0373,34.7741 C46.6763,23.2701 15.4563,25.5911 9.4403,39.9491 M96.0923,95.1921 C84.0673,95.1921 70.3623,92.6401 56.8163,87.8241 C33.5863,79.5661 14.8723,65.8521 9.1403,52.8881 C6.9843,48.0111 6.7393,43.5131 8.4133,39.5191 C11.4653,32.2311 20.5793,27.6381 34.0753,26.5851 C47.4313,25.5421 63.5293,28.0781 79.4103,33.7241 C102.6403,41.9821 121.3543,55.6951 127.0863,68.6601 C129.2423,73.5371 129.4873,78.0351 127.8133,82.0291 C124.7613,89.3171 115.6473,93.9101 102.1513,94.9631 C100.1873,95.1161 98.1653,95.1921 96.0923,95.1921\\" id=\\"Fill-8\\" fill=\\"#8CC152\\"></path>
-                      <path d=\\"M37.4075,19.5507 C26.3395,19.5507 17.6535,22.8147 13.5865,29.4827 C11.0315,33.6727 10.5065,38.7967 12.0245,44.7107 C15.7565,59.2487 30.9865,75.6817 51.7715,87.5977 C81.8655,104.8517 113.6995,106.8377 122.7345,92.0237 C125.2895,87.8337 125.8155,82.7107 124.2965,76.7967 C120.5645,62.2587 105.3345,45.8257 84.5495,33.9087 C68.0015,24.4217 50.9275,19.5507 37.4075,19.5507 M98.8585,103.0097 C85.1115,103.0097 67.8845,98.1197 51.2175,88.5647 C30.1805,76.5037 14.7495,59.8067 10.9455,44.9877 C9.3485,38.7717 9.9175,33.3597 12.6355,28.9027 C21.9805,13.5797 54.4905,15.3907 85.1035,32.9427 C106.1405,45.0027 121.5715,61.7007 125.3755,76.5187 C126.9715,82.7357 126.4035,88.1477 123.6855,92.6037 C119.4275,99.5847 110.3615,103.0097 98.8585,103.0097\\" id=\\"Fill-9\\" fill=\\"#159588\\"></path>
-                      <path d=\\"M17.2963,18.6519 L17.7333,18.9969 C5.6663,34.2729 18.5003,65.4219 46.3413,88.4339 C64.9233,103.7929 86.3753,112.2799 102.3403,110.5869 C109.4293,109.8349 115.0803,107.0269 118.6823,102.4679 C130.7493,87.1929 117.9153,56.0439 90.0753,33.0319 C71.4923,17.6719 50.0303,9.1809 34.0753,10.8779 C26.9853,11.6309 21.3343,14.4379 17.7333,18.9969 L17.2963,18.6519 Z M98.2093,111.9139 C82.5383,111.9139 62.8413,103.5179 45.6323,89.2929 C17.3273,65.8979 4.4203,34.0539 16.8593,18.3069 C20.6513,13.5069 26.5633,10.5549 33.9573,9.7699 C50.2033,8.0459 71.9803,16.6299 90.7843,32.1729 C119.0883,55.5679 131.9953,87.4119 119.5573,103.1579 C115.7653,107.9589 109.8523,110.9109 102.4573,111.6949 C101.0803,111.8409 99.6613,111.9139 98.2093,111.9139 L98.2093,111.9139 Z\\" id=\\"Fill-10\\" fill=\\"#2F81B7\\"></path>
-                      <path d=\\"M40.6234,1.2244 C32.9264,1.2244 26.4584,3.7314 21.8834,8.4964 C6.7634,24.2454 15.2924,60.4824 40.8944,89.2744 C57.5674,108.0244 78.3774,119.8714 95.2024,120.1924 C103.2004,120.3364 109.9174,117.8324 114.6264,112.9264 C129.7464,97.1774 121.2184,60.9404 95.6154,32.1484 C78.9434,13.3994 58.1344,1.5524 41.3084,1.2314 C41.0784,1.2264 40.8504,1.2244 40.6234,1.2244 M95.8884,121.3124 C95.6534,121.3124 95.4184,121.3104 95.1804,121.3064 C78.0544,120.9794 56.9344,108.9894 40.0624,90.0154 C14.0824,60.7984 5.5664,23.8834 21.0794,7.7254 C26.0104,2.5894 33.0144,-0.0516 41.3294,0.1174 C58.4554,0.4444 79.5764,12.4344 96.4484,31.4084 C122.4284,60.6244 130.9444,97.5394 115.4304,113.6984 C110.6404,118.6874 103.8944,121.3124 95.8884,121.3124\\" id=\\"Fill-11\\" fill=\\"#0056B8\\"></path>
-                  </g>
-              </g>
-          </g>
-      </svg>",
-      }
-    }
-    style={
-      Object {
-        "height": 122,
-        "margin": "0 auto",
-        "width": 137,
-      }
-    }
-  />
-  <div
-    className={undefined}
-  >
-    Loading...
-  </div>
-</div>
-`;
-
-exports[`Loading icon renders correctly with sizes 1`] = `
-<div
-  className={undefined}
-  style={
-    Object {
-      "paddingBottom": 32,
-      "paddingTop": 32,
-    }
-  }
->
-  <div
-    className={false}
-    dangerouslySetInnerHTML={
-      Object {
-        "__html": "
-      <svg id=\\"loadingIcon\\" width=\\"137px\\" height=\\"122px\\" viewBox=\\"0 0 137 122\\" version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\">
-          <g id=\\"Symbols\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\">
-              <g id=\\"Coriolis_Symbol_Large\\">
-                  <g id=\\"Group-3\\">
-                      <path d=\\"M95.8871,1.2248 C95.6601,1.2248 95.4321,1.2268 95.2021,1.2318 C78.3771,1.5528 57.5671,13.3988 40.8941,32.1488 C15.2921,60.9408 6.7641,97.1778 21.8841,112.9268 C26.5931,117.8328 33.3131,120.3458 41.3081,120.1918 C58.1341,119.8708 78.9431,108.0238 95.6161,89.2748 C121.2181,60.4828 129.7471,24.2458 114.6271,8.4968 C110.0521,3.7318 103.5841,1.2248 95.8871,1.2248 M40.6221,121.3128 C32.6151,121.3128 25.8701,118.6878 21.0801,113.6988 C5.5671,97.5398 14.0821,60.6248 40.0621,31.4078 C56.9341,12.4338 78.0551,0.4438 95.1811,0.1178 C103.5041,-0.0442 110.5001,2.5888 115.4311,7.7258 C130.9441,23.8838 122.4281,60.7988 96.4481,90.0148 C79.5761,108.9888 58.4561,120.9798 41.3301,121.3068 C41.0921,121.3108 40.8561,121.3128 40.6221,121.3128\\" id=\\"Fill-1\\" fill=\\"#E62565\\"></path>
-                      <path d=\\"M17.7337,102.4263 C21.3347,106.9853 26.9857,109.7933 34.0757,110.5453 C50.0377,112.2403 71.4927,103.7513 90.0747,88.3913 C117.9157,65.3793 130.7487,34.2303 118.6817,18.9553 C115.0807,14.3963 109.4297,11.5883 102.3397,10.8363 C86.3827,9.1433 64.9227,17.6313 46.3417,32.9893 C18.5007,56.0013 5.6667,87.1503 17.7337,102.4263 M38.2057,111.8713 C36.7527,111.8713 35.3357,111.7993 33.9577,111.6533 C26.5627,110.8683 20.6507,107.9163 16.8587,103.1163 C4.4207,87.3693 17.3277,55.5253 45.6317,32.1303 C64.4357,16.5883 86.2117,8.0043 102.4567,9.7283 C109.8517,10.5133 115.7647,13.4653 119.5567,18.2643 C131.9957,34.0113 119.0887,65.8553 90.7847,89.2513 C73.5747,103.4753 53.8767,111.8713 38.2057,111.8713\\" id=\\"Fill-2\\" fill=\\"#FF2D55\\"></path>
-                      <path d=\\"M98.9136,19.4672 C85.3926,19.4672 68.3216,24.3372 51.7716,33.8262 C30.9866,45.7422 15.7566,62.1752 12.0246,76.7122 C10.5056,82.6272 11.0316,87.7502 13.5866,91.9402 C22.6216,106.7532 54.4546,104.7682 84.5496,87.5142 C105.3346,75.5982 120.5646,59.1652 124.2966,44.6272 C125.8146,38.7132 125.2896,33.5902 122.7346,29.3992 C118.6676,22.7322 109.9786,19.4672 98.9136,19.4672 M37.4626,102.9262 C25.9576,102.9262 16.8936,99.5022 12.6356,92.5202 C9.9176,88.0642 9.3486,82.6522 10.9456,76.4352 C14.7496,61.6172 30.1806,44.9192 51.2176,32.8592 C81.8306,15.3072 114.3406,13.4952 123.6856,28.8192 C126.4036,33.2762 126.9716,38.6882 125.3756,44.9042 C121.5716,59.7232 106.1406,76.4202 85.1036,88.4812 C68.4386,98.0352 51.2086,102.9262 37.4626,102.9262\\" id=\\"Fill-3\\" fill=\\"#FC5830\\"></path>
-                      <path d=\\"M9.4403,81.4743 C15.4543,95.8313 46.6743,98.1533 79.0373,86.6483 C101.9923,78.4883 120.4523,65.0103 126.0673,52.3123 C128.0943,47.7273 128.3363,43.5253 126.7863,39.8243 C120.7703,25.4663 89.5513,23.1453 57.1893,34.6493 C34.2343,42.8093 15.7733,56.2873 10.1593,68.9863 C8.1323,73.5713 7.8903,77.7733 9.4403,81.4743 M40.1333,95.0673 C38.0613,95.0673 36.0383,94.9913 34.0753,94.8383 C20.5793,93.7853 11.4653,89.1923 8.4133,81.9043 C6.7393,77.9103 6.9843,73.4123 9.1403,68.5353 C14.8723,55.5713 33.5863,41.8573 56.8163,33.5993 C72.6973,27.9533 88.7963,25.4183 102.1513,26.4603 C115.6473,27.5133 124.7613,32.1063 127.8133,39.3943 C129.4873,43.3883 129.2423,47.8863 127.0863,52.7633 C121.3543,65.7273 102.6403,79.4403 79.4103,87.6983 C65.8643,92.5143 52.1583,95.0673 40.1333,95.0673\\" id=\\"Fill-4\\" fill=\\"#FD9727\\"></path>
-                      <path d=\\"M5.2865,71.0308 C8.2875,84.9278 38.9025,91.5548 73.5305,85.8008 C99.2035,81.5358 120.7115,71.5368 128.3265,60.3278 C130.6935,56.8438 131.5405,53.4458 130.8455,50.2258 C127.8445,36.3288 97.2315,29.7038 62.6025,35.4558 C36.9295,39.7208 15.4205,49.7188 7.8045,60.9288 C5.4385,64.4118 4.5915,67.8108 5.2865,71.0308 M49.2235,88.9908 C41.2215,88.9908 33.7145,88.2598 27.0975,86.7998 C13.8625,83.8788 5.7295,78.3618 4.1975,71.2658 C3.4355,67.7368 4.3385,64.0478 6.8835,60.3028 C14.6615,48.8538 36.4605,38.6688 62.4195,34.3568 C79.4025,31.5348 95.9575,31.5708 109.0355,34.4568 C122.2695,37.3778 130.4015,42.8948 131.9345,49.9908 C132.6965,53.5198 131.7935,57.2078 129.2485,60.9538 C121.4705,72.4028 99.6715,82.5878 73.7125,86.8998 C65.3225,88.2938 57.0375,88.9908 49.2235,88.9908\\" id=\\"Fill-5\\" fill=\\"#FDC02F\\"></path>
-                      <path d=\\"M68.0188,36.4536 C31.1278,36.4536 1.1138,47.3826 1.1138,60.8156 C1.1138,74.2496 31.1278,85.1786 68.0188,85.1786 C104.9098,85.1786 134.9228,74.2496 134.9228,60.8156 C134.9228,47.3826 104.9098,36.4536 68.0188,36.4536 M68.0188,86.2926 C49.9348,86.2926 32.9248,83.6876 20.1228,78.9596 C7.1468,74.1656 -0.0002,67.7226 -0.0002,60.8156 C-0.0002,53.9096 7.1468,47.4656 20.1228,42.6726 C32.9248,37.9436 49.9348,35.3396 68.0188,35.3396 C86.1028,35.3396 103.1118,37.9436 115.9138,42.6726 C128.8908,47.4656 136.0368,53.9096 136.0368,60.8156 C136.0368,67.7226 128.8908,74.1656 115.9138,78.9596 C103.1128,83.6876 86.1028,86.2926 68.0188,86.2926\\" id=\\"Fill-6\\" fill=\\"#FFDC00\\"></path>
-                      <path d=\\"M6.35082759,49.9596832 C5.65582759,53.1796832 6.50282759,56.5786832 8.86882759,60.0616832 C16.4848276,71.2716832 37.9938276,81.2696832 63.6668276,85.5346832 C72.1398276,86.9426832 80.3768276,87.6086832 88.0238276,87.6096832 C111.627828,87.6096832 129.642828,81.2616832 131.909828,70.7646832 C132.604828,67.5446832 131.757828,64.1466832 129.390828,60.6636832 C121.776828,49.4536832 100.267828,39.4546832 74.5948276,35.1896832 C39.9688276,29.4386832 9.35182759,36.0626832 6.35082759,49.9596832 M87.9738276,88.7246832 C80.1598276,88.7246832 71.8748276,88.0276832 63.4838276,86.6336832 C37.5248276,82.3216832 15.7258276,72.1376832 7.94782759,60.6876832 C5.40282759,56.9426832 4.49982759,53.2536832 5.26182759,49.7246832 C6.79382759,42.6286832 14.9268276,37.1126832 28.1618276,34.1916832 C41.2388276,31.3046832 57.7938276,31.2686832 74.7768276,34.0906832 C100.735828,38.4036832 122.534828,48.5876832 130.313828,60.0366832 C132.857828,63.7826832 133.760828,67.4706832 132.998828,70.9996832 C131.465828,78.0956832 123.333828,83.6126832 110.099828,86.5336832 C103.482828,87.9936832 95.9758276,88.7246832 87.9738276,88.7246832\\" id=\\"Fill-7\\" fill=\\"#CDDA49\\"></path>
-                      <path d=\\"M9.4403,39.9491 C7.8903,43.6501 8.1323,47.8521 10.1593,52.4371 C15.7733,65.1361 34.2343,78.6141 57.1893,86.7741 C89.5503,98.2791 120.7713,95.9561 126.7863,81.5991 C128.3363,77.8981 128.0943,73.6961 126.0673,69.1111 C120.4523,56.4121 101.9923,42.9341 79.0373,34.7741 C46.6763,23.2701 15.4563,25.5911 9.4403,39.9491 M96.0923,95.1921 C84.0673,95.1921 70.3623,92.6401 56.8163,87.8241 C33.5863,79.5661 14.8723,65.8521 9.1403,52.8881 C6.9843,48.0111 6.7393,43.5131 8.4133,39.5191 C11.4653,32.2311 20.5793,27.6381 34.0753,26.5851 C47.4313,25.5421 63.5293,28.0781 79.4103,33.7241 C102.6403,41.9821 121.3543,55.6951 127.0863,68.6601 C129.2423,73.5371 129.4873,78.0351 127.8133,82.0291 C124.7613,89.3171 115.6473,93.9101 102.1513,94.9631 C100.1873,95.1161 98.1653,95.1921 96.0923,95.1921\\" id=\\"Fill-8\\" fill=\\"#8CC152\\"></path>
-                      <path d=\\"M37.4075,19.5507 C26.3395,19.5507 17.6535,22.8147 13.5865,29.4827 C11.0315,33.6727 10.5065,38.7967 12.0245,44.7107 C15.7565,59.2487 30.9865,75.6817 51.7715,87.5977 C81.8655,104.8517 113.6995,106.8377 122.7345,92.0237 C125.2895,87.8337 125.8155,82.7107 124.2965,76.7967 C120.5645,62.2587 105.3345,45.8257 84.5495,33.9087 C68.0015,24.4217 50.9275,19.5507 37.4075,19.5507 M98.8585,103.0097 C85.1115,103.0097 67.8845,98.1197 51.2175,88.5647 C30.1805,76.5037 14.7495,59.8067 10.9455,44.9877 C9.3485,38.7717 9.9175,33.3597 12.6355,28.9027 C21.9805,13.5797 54.4905,15.3907 85.1035,32.9427 C106.1405,45.0027 121.5715,61.7007 125.3755,76.5187 C126.9715,82.7357 126.4035,88.1477 123.6855,92.6037 C119.4275,99.5847 110.3615,103.0097 98.8585,103.0097\\" id=\\"Fill-9\\" fill=\\"#159588\\"></path>
-                      <path d=\\"M17.2963,18.6519 L17.7333,18.9969 C5.6663,34.2729 18.5003,65.4219 46.3413,88.4339 C64.9233,103.7929 86.3753,112.2799 102.3403,110.5869 C109.4293,109.8349 115.0803,107.0269 118.6823,102.4679 C130.7493,87.1929 117.9153,56.0439 90.0753,33.0319 C71.4923,17.6719 50.0303,9.1809 34.0753,10.8779 C26.9853,11.6309 21.3343,14.4379 17.7333,18.9969 L17.2963,18.6519 Z M98.2093,111.9139 C82.5383,111.9139 62.8413,103.5179 45.6323,89.2929 C17.3273,65.8979 4.4203,34.0539 16.8593,18.3069 C20.6513,13.5069 26.5633,10.5549 33.9573,9.7699 C50.2033,8.0459 71.9803,16.6299 90.7843,32.1729 C119.0883,55.5679 131.9953,87.4119 119.5573,103.1579 C115.7653,107.9589 109.8523,110.9109 102.4573,111.6949 C101.0803,111.8409 99.6613,111.9139 98.2093,111.9139 L98.2093,111.9139 Z\\" id=\\"Fill-10\\" fill=\\"#2F81B7\\"></path>
-                      <path d=\\"M40.6234,1.2244 C32.9264,1.2244 26.4584,3.7314 21.8834,8.4964 C6.7634,24.2454 15.2924,60.4824 40.8944,89.2744 C57.5674,108.0244 78.3774,119.8714 95.2024,120.1924 C103.2004,120.3364 109.9174,117.8324 114.6264,112.9264 C129.7464,97.1774 121.2184,60.9404 95.6154,32.1484 C78.9434,13.3994 58.1344,1.5524 41.3084,1.2314 C41.0784,1.2264 40.8504,1.2244 40.6234,1.2244 M95.8884,121.3124 C95.6534,121.3124 95.4184,121.3104 95.1804,121.3064 C78.0544,120.9794 56.9344,108.9894 40.0624,90.0154 C14.0824,60.7984 5.5664,23.8834 21.0794,7.7254 C26.0104,2.5894 33.0144,-0.0516 41.3294,0.1174 C58.4554,0.4444 79.5764,12.4344 96.4484,31.4084 C122.4284,60.6244 130.9444,97.5394 115.4304,113.6984 C110.6404,118.6874 103.8944,121.3124 95.8884,121.3124\\" id=\\"Fill-11\\" fill=\\"#0056B8\\"></path>
-                  </g>
-              </g>
-          </g>
-      </svg>",
-      }
-    }
-    style={
-      Object {
-        "height": 200,
-        "margin": "0 auto",
-        "width": 300,
-      }
-    }
-  />
-  <div
-    className={undefined}
-  >
-    Loading...
-  </div>
-</div>
-`;
-
-exports[`Loading icon renders correctly with text 1`] = `
-<div
-  className={undefined}
-  style={
-    Object {
-      "paddingBottom": 32,
-      "paddingTop": 32,
-    }
-  }
->
-  <div
-    className={false}
-    dangerouslySetInnerHTML={
-      Object {
-        "__html": "
-      <svg id=\\"loadingIcon\\" width=\\"137px\\" height=\\"122px\\" viewBox=\\"0 0 137 122\\" version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\">
-          <g id=\\"Symbols\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\">
-              <g id=\\"Coriolis_Symbol_Large\\">
-                  <g id=\\"Group-3\\">
-                      <path d=\\"M95.8871,1.2248 C95.6601,1.2248 95.4321,1.2268 95.2021,1.2318 C78.3771,1.5528 57.5671,13.3988 40.8941,32.1488 C15.2921,60.9408 6.7641,97.1778 21.8841,112.9268 C26.5931,117.8328 33.3131,120.3458 41.3081,120.1918 C58.1341,119.8708 78.9431,108.0238 95.6161,89.2748 C121.2181,60.4828 129.7471,24.2458 114.6271,8.4968 C110.0521,3.7318 103.5841,1.2248 95.8871,1.2248 M40.6221,121.3128 C32.6151,121.3128 25.8701,118.6878 21.0801,113.6988 C5.5671,97.5398 14.0821,60.6248 40.0621,31.4078 C56.9341,12.4338 78.0551,0.4438 95.1811,0.1178 C103.5041,-0.0442 110.5001,2.5888 115.4311,7.7258 C130.9441,23.8838 122.4281,60.7988 96.4481,90.0148 C79.5761,108.9888 58.4561,120.9798 41.3301,121.3068 C41.0921,121.3108 40.8561,121.3128 40.6221,121.3128\\" id=\\"Fill-1\\" fill=\\"#E62565\\"></path>
-                      <path d=\\"M17.7337,102.4263 C21.3347,106.9853 26.9857,109.7933 34.0757,110.5453 C50.0377,112.2403 71.4927,103.7513 90.0747,88.3913 C117.9157,65.3793 130.7487,34.2303 118.6817,18.9553 C115.0807,14.3963 109.4297,11.5883 102.3397,10.8363 C86.3827,9.1433 64.9227,17.6313 46.3417,32.9893 C18.5007,56.0013 5.6667,87.1503 17.7337,102.4263 M38.2057,111.8713 C36.7527,111.8713 35.3357,111.7993 33.9577,111.6533 C26.5627,110.8683 20.6507,107.9163 16.8587,103.1163 C4.4207,87.3693 17.3277,55.5253 45.6317,32.1303 C64.4357,16.5883 86.2117,8.0043 102.4567,9.7283 C109.8517,10.5133 115.7647,13.4653 119.5567,18.2643 C131.9957,34.0113 119.0887,65.8553 90.7847,89.2513 C73.5747,103.4753 53.8767,111.8713 38.2057,111.8713\\" id=\\"Fill-2\\" fill=\\"#FF2D55\\"></path>
-                      <path d=\\"M98.9136,19.4672 C85.3926,19.4672 68.3216,24.3372 51.7716,33.8262 C30.9866,45.7422 15.7566,62.1752 12.0246,76.7122 C10.5056,82.6272 11.0316,87.7502 13.5866,91.9402 C22.6216,106.7532 54.4546,104.7682 84.5496,87.5142 C105.3346,75.5982 120.5646,59.1652 124.2966,44.6272 C125.8146,38.7132 125.2896,33.5902 122.7346,29.3992 C118.6676,22.7322 109.9786,19.4672 98.9136,19.4672 M37.4626,102.9262 C25.9576,102.9262 16.8936,99.5022 12.6356,92.5202 C9.9176,88.0642 9.3486,82.6522 10.9456,76.4352 C14.7496,61.6172 30.1806,44.9192 51.2176,32.8592 C81.8306,15.3072 114.3406,13.4952 123.6856,28.8192 C126.4036,33.2762 126.9716,38.6882 125.3756,44.9042 C121.5716,59.7232 106.1406,76.4202 85.1036,88.4812 C68.4386,98.0352 51.2086,102.9262 37.4626,102.9262\\" id=\\"Fill-3\\" fill=\\"#FC5830\\"></path>
-                      <path d=\\"M9.4403,81.4743 C15.4543,95.8313 46.6743,98.1533 79.0373,86.6483 C101.9923,78.4883 120.4523,65.0103 126.0673,52.3123 C128.0943,47.7273 128.3363,43.5253 126.7863,39.8243 C120.7703,25.4663 89.5513,23.1453 57.1893,34.6493 C34.2343,42.8093 15.7733,56.2873 10.1593,68.9863 C8.1323,73.5713 7.8903,77.7733 9.4403,81.4743 M40.1333,95.0673 C38.0613,95.0673 36.0383,94.9913 34.0753,94.8383 C20.5793,93.7853 11.4653,89.1923 8.4133,81.9043 C6.7393,77.9103 6.9843,73.4123 9.1403,68.5353 C14.8723,55.5713 33.5863,41.8573 56.8163,33.5993 C72.6973,27.9533 88.7963,25.4183 102.1513,26.4603 C115.6473,27.5133 124.7613,32.1063 127.8133,39.3943 C129.4873,43.3883 129.2423,47.8863 127.0863,52.7633 C121.3543,65.7273 102.6403,79.4403 79.4103,87.6983 C65.8643,92.5143 52.1583,95.0673 40.1333,95.0673\\" id=\\"Fill-4\\" fill=\\"#FD9727\\"></path>
-                      <path d=\\"M5.2865,71.0308 C8.2875,84.9278 38.9025,91.5548 73.5305,85.8008 C99.2035,81.5358 120.7115,71.5368 128.3265,60.3278 C130.6935,56.8438 131.5405,53.4458 130.8455,50.2258 C127.8445,36.3288 97.2315,29.7038 62.6025,35.4558 C36.9295,39.7208 15.4205,49.7188 7.8045,60.9288 C5.4385,64.4118 4.5915,67.8108 5.2865,71.0308 M49.2235,88.9908 C41.2215,88.9908 33.7145,88.2598 27.0975,86.7998 C13.8625,83.8788 5.7295,78.3618 4.1975,71.2658 C3.4355,67.7368 4.3385,64.0478 6.8835,60.3028 C14.6615,48.8538 36.4605,38.6688 62.4195,34.3568 C79.4025,31.5348 95.9575,31.5708 109.0355,34.4568 C122.2695,37.3778 130.4015,42.8948 131.9345,49.9908 C132.6965,53.5198 131.7935,57.2078 129.2485,60.9538 C121.4705,72.4028 99.6715,82.5878 73.7125,86.8998 C65.3225,88.2938 57.0375,88.9908 49.2235,88.9908\\" id=\\"Fill-5\\" fill=\\"#FDC02F\\"></path>
-                      <path d=\\"M68.0188,36.4536 C31.1278,36.4536 1.1138,47.3826 1.1138,60.8156 C1.1138,74.2496 31.1278,85.1786 68.0188,85.1786 C104.9098,85.1786 134.9228,74.2496 134.9228,60.8156 C134.9228,47.3826 104.9098,36.4536 68.0188,36.4536 M68.0188,86.2926 C49.9348,86.2926 32.9248,83.6876 20.1228,78.9596 C7.1468,74.1656 -0.0002,67.7226 -0.0002,60.8156 C-0.0002,53.9096 7.1468,47.4656 20.1228,42.6726 C32.9248,37.9436 49.9348,35.3396 68.0188,35.3396 C86.1028,35.3396 103.1118,37.9436 115.9138,42.6726 C128.8908,47.4656 136.0368,53.9096 136.0368,60.8156 C136.0368,67.7226 128.8908,74.1656 115.9138,78.9596 C103.1128,83.6876 86.1028,86.2926 68.0188,86.2926\\" id=\\"Fill-6\\" fill=\\"#FFDC00\\"></path>
-                      <path d=\\"M6.35082759,49.9596832 C5.65582759,53.1796832 6.50282759,56.5786832 8.86882759,60.0616832 C16.4848276,71.2716832 37.9938276,81.2696832 63.6668276,85.5346832 C72.1398276,86.9426832 80.3768276,87.6086832 88.0238276,87.6096832 C111.627828,87.6096832 129.642828,81.2616832 131.909828,70.7646832 C132.604828,67.5446832 131.757828,64.1466832 129.390828,60.6636832 C121.776828,49.4536832 100.267828,39.4546832 74.5948276,35.1896832 C39.9688276,29.4386832 9.35182759,36.0626832 6.35082759,49.9596832 M87.9738276,88.7246832 C80.1598276,88.7246832 71.8748276,88.0276832 63.4838276,86.6336832 C37.5248276,82.3216832 15.7258276,72.1376832 7.94782759,60.6876832 C5.40282759,56.9426832 4.49982759,53.2536832 5.26182759,49.7246832 C6.79382759,42.6286832 14.9268276,37.1126832 28.1618276,34.1916832 C41.2388276,31.3046832 57.7938276,31.2686832 74.7768276,34.0906832 C100.735828,38.4036832 122.534828,48.5876832 130.313828,60.0366832 C132.857828,63.7826832 133.760828,67.4706832 132.998828,70.9996832 C131.465828,78.0956832 123.333828,83.6126832 110.099828,86.5336832 C103.482828,87.9936832 95.9758276,88.7246832 87.9738276,88.7246832\\" id=\\"Fill-7\\" fill=\\"#CDDA49\\"></path>
-                      <path d=\\"M9.4403,39.9491 C7.8903,43.6501 8.1323,47.8521 10.1593,52.4371 C15.7733,65.1361 34.2343,78.6141 57.1893,86.7741 C89.5503,98.2791 120.7713,95.9561 126.7863,81.5991 C128.3363,77.8981 128.0943,73.6961 126.0673,69.1111 C120.4523,56.4121 101.9923,42.9341 79.0373,34.7741 C46.6763,23.2701 15.4563,25.5911 9.4403,39.9491 M96.0923,95.1921 C84.0673,95.1921 70.3623,92.6401 56.8163,87.8241 C33.5863,79.5661 14.8723,65.8521 9.1403,52.8881 C6.9843,48.0111 6.7393,43.5131 8.4133,39.5191 C11.4653,32.2311 20.5793,27.6381 34.0753,26.5851 C47.4313,25.5421 63.5293,28.0781 79.4103,33.7241 C102.6403,41.9821 121.3543,55.6951 127.0863,68.6601 C129.2423,73.5371 129.4873,78.0351 127.8133,82.0291 C124.7613,89.3171 115.6473,93.9101 102.1513,94.9631 C100.1873,95.1161 98.1653,95.1921 96.0923,95.1921\\" id=\\"Fill-8\\" fill=\\"#8CC152\\"></path>
-                      <path d=\\"M37.4075,19.5507 C26.3395,19.5507 17.6535,22.8147 13.5865,29.4827 C11.0315,33.6727 10.5065,38.7967 12.0245,44.7107 C15.7565,59.2487 30.9865,75.6817 51.7715,87.5977 C81.8655,104.8517 113.6995,106.8377 122.7345,92.0237 C125.2895,87.8337 125.8155,82.7107 124.2965,76.7967 C120.5645,62.2587 105.3345,45.8257 84.5495,33.9087 C68.0015,24.4217 50.9275,19.5507 37.4075,19.5507 M98.8585,103.0097 C85.1115,103.0097 67.8845,98.1197 51.2175,88.5647 C30.1805,76.5037 14.7495,59.8067 10.9455,44.9877 C9.3485,38.7717 9.9175,33.3597 12.6355,28.9027 C21.9805,13.5797 54.4905,15.3907 85.1035,32.9427 C106.1405,45.0027 121.5715,61.7007 125.3755,76.5187 C126.9715,82.7357 126.4035,88.1477 123.6855,92.6037 C119.4275,99.5847 110.3615,103.0097 98.8585,103.0097\\" id=\\"Fill-9\\" fill=\\"#159588\\"></path>
-                      <path d=\\"M17.2963,18.6519 L17.7333,18.9969 C5.6663,34.2729 18.5003,65.4219 46.3413,88.4339 C64.9233,103.7929 86.3753,112.2799 102.3403,110.5869 C109.4293,109.8349 115.0803,107.0269 118.6823,102.4679 C130.7493,87.1929 117.9153,56.0439 90.0753,33.0319 C71.4923,17.6719 50.0303,9.1809 34.0753,10.8779 C26.9853,11.6309 21.3343,14.4379 17.7333,18.9969 L17.2963,18.6519 Z M98.2093,111.9139 C82.5383,111.9139 62.8413,103.5179 45.6323,89.2929 C17.3273,65.8979 4.4203,34.0539 16.8593,18.3069 C20.6513,13.5069 26.5633,10.5549 33.9573,9.7699 C50.2033,8.0459 71.9803,16.6299 90.7843,32.1729 C119.0883,55.5679 131.9953,87.4119 119.5573,103.1579 C115.7653,107.9589 109.8523,110.9109 102.4573,111.6949 C101.0803,111.8409 99.6613,111.9139 98.2093,111.9139 L98.2093,111.9139 Z\\" id=\\"Fill-10\\" fill=\\"#2F81B7\\"></path>
-                      <path d=\\"M40.6234,1.2244 C32.9264,1.2244 26.4584,3.7314 21.8834,8.4964 C6.7634,24.2454 15.2924,60.4824 40.8944,89.2744 C57.5674,108.0244 78.3774,119.8714 95.2024,120.1924 C103.2004,120.3364 109.9174,117.8324 114.6264,112.9264 C129.7464,97.1774 121.2184,60.9404 95.6154,32.1484 C78.9434,13.3994 58.1344,1.5524 41.3084,1.2314 C41.0784,1.2264 40.8504,1.2244 40.6234,1.2244 M95.8884,121.3124 C95.6534,121.3124 95.4184,121.3104 95.1804,121.3064 C78.0544,120.9794 56.9344,108.9894 40.0624,90.0154 C14.0824,60.7984 5.5664,23.8834 21.0794,7.7254 C26.0104,2.5894 33.0144,-0.0516 41.3294,0.1174 C58.4554,0.4444 79.5764,12.4344 96.4484,31.4084 C122.4284,60.6244 130.9444,97.5394 115.4304,113.6984 C110.6404,118.6874 103.8944,121.3124 95.8884,121.3124\\" id=\\"Fill-11\\" fill=\\"#0056B8\\"></path>
-                  </g>
-              </g>
-          </g>
-      </svg>",
-      }
-    }
-    style={
-      Object {
-        "height": 122,
-        "margin": "0 auto",
-        "width": 137,
-      }
-    }
-  />
-  <div
-    className={undefined}
-  >
-    Test text
-  </div>
-</div>
-`;

Plik diff jest za duży
+ 0 - 77
src/__tests__/__snapshots__/LoginPage.test.js.snap


+ 0 - 39
src/__tests__/__snapshots__/ProgressBar.test.js.snap

@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Progress Bar renders correctly 1`] = `
-<div
-  className={undefined}
->
-  <div
-    className={undefined}
-  >
-    <div
-      className="undefined "
-      style={
-        Object {
-          "width": "0%",
-        }
-      }
-    />
-  </div>
-</div>
-`;
-
-exports[`Progress Bar renders progress correctly 1`] = `
-<div
-  className={undefined}
->
-  <div
-    className={undefined}
-  >
-    <div
-      className="undefined "
-      style={
-        Object {
-          "width": "20%",
-        }
-      }
-    />
-  </div>
-</div>
-`;

+ 0 - 0
src/actions/.gitignore


+ 0 - 232
src/actions/ConnectionsActions/ConnectionsActions.js

@@ -1,232 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-import Reflux from 'reflux';
-import Api from '../../components/ApiCaller';
-import { servicesUrl, useSecret } from '../../config';
-
-let ConnectionsActions = Reflux.createActions({
-  loadConnections: { children: ['completed', 'failed'] },
-  loadConnectionDetail: { children: ['completed', 'failed'] },
-  loadProviders: { children: ['completed', 'failed'] },
-  loadInstances: { children: ['completed', 'failed'] },
-  loadInstanceDetail: { children: ['completed', 'failed'] },
-  loadNetworks: { children: ['completed', 'failed'] },
-  newEndpoint: { children: ['success', 'failed'] },
-  saveEndpoint: { children: ['success', 'failed'] },
-  editEndpoint: { children: ['success', 'failed'] },
-  saveEditEndpoint: { children: ['success', 'failed'] },
-  validateConnection: { children: ['completed', 'failed'] },
-  deleteConnection: { children: ['completed', 'failed'] },
-  loadProviderType: {},
-  assignConnectionProvider: {},
-  updateProvider: {},
-  getSourceClouds: {},
-  getTargetClouds: {},
-  resetSelections: {},
-  setConnection: {}
-})
-
-
-ConnectionsActions.loadConnections.listen(() => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  if (projectId) {
-    Api.sendAjaxRequest({
-      url: `${servicesUrl.coriolis}/${projectId}/endpoints`,
-      method: "GET"
-    }).then(ConnectionsActions.loadConnections.completed, ConnectionsActions.loadConnections.failed)
-      .catch(ConnectionsActions.loadConnections.failed);
-  }
-})
-
-ConnectionsActions.loadConnections.shouldEmit = () => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  return typeof projectId !== "undefined"
-}
-
-ConnectionsActions.loadProviders.listen(() => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/providers`,
-    method: "GET"
-  }).then(response => {
-    ConnectionsActions.loadProviders.completed(response)
-  }, ConnectionsActions.loadProviders.failed)
-    .catch(ConnectionsActions.loadProviders.failed);
-})
-
-ConnectionsActions.loadNetworks.listen((endpoint) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  let targetEnv = btoa(JSON.stringify(Reflux.GlobalState.wizardStore.destination_environment))
-  let url = `${servicesUrl.coriolis}/${projectId}/endpoints/${endpoint.id}/networks?env=${targetEnv}`
-
-  Api.sendAjaxRequest({
-    url: url,
-    method: "GET"
-  }).then(ConnectionsActions.loadNetworks.completed, ConnectionsActions.loadNetworks.failed)
-    .catch(ConnectionsActions.loadNetworks.failed);
-})
-
-function getBarbicanPayload(data) {
-  return {
-    payload: JSON.stringify(data),
-    payload_content_type: "text/plain",
-    algorithm: "aes",
-    bit_length: 256,
-    mode: "cbc",
-    content_types: {
-      default: "text/plain"
-    }
-  }
-}
-
-ConnectionsActions.newEndpoint.listen((data, callback = null) => {
-  if (useSecret) {
-    Api.sendAjaxRequest({
-      url: servicesUrl.barbican + "/v1/secrets",
-      method: "POST",
-      data: getBarbicanPayload(data.connection_info)
-    }).then((response) => {
-      ConnectionsActions.newEndpoint.success(response, data, callback)
-    }, ConnectionsActions.newEndpoint.failed)
-      .catch(ConnectionsActions.newEndpoint.failed);
-  } else {
-    ConnectionsActions.saveEndpoint(data)
-  }
-});
-
-ConnectionsActions.saveEndpoint.listen((data, secretRef, callback) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  let payload = null
-  if (useSecret) {
-    payload = {
-      endpoint: {
-        name: data.name,
-        description: data.description,
-        type: data.type,
-        connection_info: {
-          secret_ref: secretRef
-        }
-      }
-    }
-  } else {
-    payload = { endpoint: data }
-  }
-
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/endpoints`,
-    method: "POST",
-    data: payload
-  }).then((response) => {
-    ConnectionsActions.saveEndpoint.success(response, callback)
-  }, ConnectionsActions.saveEndpoint.failed)
-    .catch(ConnectionsActions.saveEndpoint.failed);
-})
-
-ConnectionsActions.editEndpoint.listen((connection, data, callback = null) => {
-  if (connection.connection_info && connection.connection_info.secret_ref) {
-    let uuidIndex = connection.connection_info.secret_ref.lastIndexOf("/")
-    let uuid = connection.connection_info.secret_ref.substr(uuidIndex + 1)
-    Api.sendAjaxRequest({
-      url: servicesUrl.barbican + "/v1/secrets/" + uuid,
-      method: "DELETE"
-    })
-
-    Api.sendAjaxRequest({
-      url: servicesUrl.barbican + "/v1/secrets",
-      method: "POST",
-      data: getBarbicanPayload(data.connection_info)
-    }).then((response) => {
-      ConnectionsActions.editEndpoint.success(response, connection, data, callback)
-    }, ConnectionsActions.editEndpoint.failed)
-      .catch(ConnectionsActions.editEndpoint.failed);
-  } else {
-    ConnectionsActions.saveEditEndpoint(connection, data, callback)
-  }
-});
-
-ConnectionsActions.saveEditEndpoint.listen((connection, data, callback = null) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  let payload = {
-    endpoint: {
-      name: data.name,
-      description: data.description,
-      connection_info: data.connection_info
-    }
-  }
-
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/endpoints/${connection.id}`,
-    method: "PUT",
-    data: payload
-  }).then((response) => {
-    if (typeof callback === "function") {
-      callback(response)
-    }
-    ConnectionsActions.saveEditEndpoint.success(response)
-  }, ConnectionsActions.saveEditEndpoint.failed)
-})
-
-ConnectionsActions.validateConnection.listen((endpoint, callback, failCallback) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/endpoints/${endpoint.id}/actions`,
-    method: "POST",
-    data: { "validate-connection": null }
-  }).then(response => {
-    if (callback) {
-      callback(response)
-    }
-    ConnectionsActions.validateConnection.completed(response)
-  }, (response) => {
-    if (typeof failCallback == "function") {
-      failCallback(response)
-    }
-    ConnectionsActions.validateConnection.failed()
-  })
-    .catch((response) => {
-      if (typeof failCallback == "function") {
-        failCallback(response)
-      }
-      ConnectionsActions.validateConnection.failed()
-    });
-})
-
-ConnectionsActions.deleteConnection.listen((connection) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/endpoints/${connection.id}`,
-    method: "DELETE"
-  }).then(() => {
-    if (connection.connection_info && connection.connection_info.secret_ref) {
-      let uuidIndex = connection.connection_info.secret_ref.lastIndexOf("/")
-      let uuid = connection.connection_info.secret_ref.substr(uuidIndex + 1)
-      Api.sendAjaxRequest({
-        url: servicesUrl.barbican + "/v1/secrets/" + uuid,
-        method: "DELETE"
-      }).then(() => { ConnectionsActions.deleteConnection.completed(connection) },
-        ConnectionsActions.deleteConnection.failed)
-    } else {
-      ConnectionsActions.deleteConnection.completed(connection)
-    }
-  }, ConnectionsActions.deleteConnection.failed)
-    .catch(ConnectionsActions.deleteConnection.failed);
-})
-
-export default ConnectionsActions;

+ 0 - 6
src/actions/ConnectionsActions/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "ConnectionsActions",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./ConnectionsActions.js"
-}

+ 128 - 0
src/actions/EndpointActions.js

@@ -0,0 +1,128 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import EndpointSource from '../sources/EndpointSource'
+
+class EndpointActions {
+  getEndpoints(options) {
+    EndpointSource.getEndpoints().then(
+      endpoints => { this.getEndpointsCompleted(endpoints) },
+      response => { this.getEndpointsFailed(response) }
+    )
+
+    return options || true
+  }
+
+  getEndpointsCompleted(endpoints) {
+    return endpoints
+  }
+
+  getEndpointsFailed(response) {
+    return response || true
+  }
+
+  delete(endpoint) {
+    EndpointSource.delete(endpoint).then(
+      () => { this.deleteSuccess(endpoint.id) },
+      response => { this.deleteFailed(response) },
+    )
+    return endpoint
+  }
+
+  deleteSuccess(endpointId) {
+    return endpointId
+  }
+
+  deleteFailed(response) {
+    return response || true
+  }
+
+  getConnectionInfo(endpoint) {
+    EndpointSource.getConnectionInfo(endpoint).then(
+      connectionInfo => { this.getConnectionInfoSuccess(connectionInfo) },
+      response => { this.getConnectionInfoFailed(response) },
+    )
+    return endpoint || true
+  }
+
+  getConnectionInfoSuccess(connectionInfo) {
+    return connectionInfo
+  }
+
+  getConnectionInfoFailed(response) {
+    return response || true
+  }
+
+  validate(endpoint) {
+    EndpointSource.validate(endpoint).then(
+      validation => { this.validateSuccess(validation) },
+      response => { this.validateFailed(response) },
+    )
+    return endpoint
+  }
+
+  validateSuccess(validation) {
+    return validation
+  }
+
+  validateFailed(response) {
+    return response || true
+  }
+
+  clearValidation() {
+    return true
+  }
+
+  update(endpoint) {
+    EndpointSource.update(endpoint).then(
+      endpointResponse => { this.updateSuccess(endpointResponse) },
+      response => { this.updateFailed(response) },
+    )
+
+    return endpoint
+  }
+
+  updateSuccess(endpoint) {
+    return endpoint
+  }
+
+  updateFailed(response) {
+    return response || true
+  }
+
+  clearConnectionInfo() {
+    return true
+  }
+
+  add(endpoint) {
+    EndpointSource.add(endpoint).then(
+      endpointResponse => { this.addSuccess(endpointResponse) },
+      response => { this.addFailed(response) },
+    )
+
+    return endpoint
+  }
+
+  addSuccess(endpoint) {
+    return endpoint
+  }
+
+  addFailed(response) {
+    return response || true
+  }
+}
+
+export default alt.createActions(EndpointActions)

+ 134 - 0
src/actions/InstanceActions.js

@@ -0,0 +1,134 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import InstanceSource from '../sources/InstanceSource'
+import InstanceStore from '../stores/InstanceStore'
+import { wizardConfig } from '../config'
+
+class InstanceActions {
+  loadInstances(endpointId) {
+    InstanceSource.loadInstances(endpointId).then(
+      instances => { this.loadInstancesSuccess(instances) },
+      response => { this.loadInstancesFailed(response) },
+    )
+    return true
+  }
+
+  loadInstancesSuccess(instances) {
+    return instances
+  }
+
+  loadInstancesFailed(response) {
+    return response || true
+  }
+
+  searchInstances(endpointId, searchText) {
+    InstanceSource.loadInstances(endpointId, searchText).then(
+      instances => { this.searchInstancesSuccess(instances, searchText) },
+      response => { this.searchInstancesFailed(response) },
+    )
+    return true
+  }
+
+  searchInstancesSuccess(instances, searchText) {
+    return { instances, searchText }
+  }
+
+  searchInstancesFailed(response) {
+    return response || true
+  }
+
+  loadNextPage(endpointId, searchText) {
+    let instanceStore = InstanceStore.getState()
+
+    if (instanceStore.cachedInstances.length > wizardConfig.instancesItemsPerPage * instanceStore.currentPage) {
+      return { fromCache: true }
+    }
+
+    InstanceSource.loadInstances(
+      endpointId,
+      searchText,
+      instanceStore.instances[instanceStore.instances.length - 1].id
+    ).then(
+      instances => { this.loadNextPageSuccess(instances) },
+      response => { this.loadNextPageFailed(response) },
+    )
+    return { fromCache: false }
+  }
+
+  loadNextPageFromCache() {
+    return true
+  }
+
+  loadNextPageSuccess(instances) {
+    return instances
+  }
+
+  loadNextPageFailed(response) {
+    return response || true
+  }
+
+  loadPreviousPage() {
+    return true
+  }
+
+  reloadInstances(endpointId, searchText) {
+    InstanceSource.loadInstances(endpointId, searchText).then(
+      instances => { this.reloadInstancesSuccess(instances, searchText) },
+      response => { this.reloadInstancesFailed(response) },
+    )
+
+    return true
+  }
+
+  reloadInstancesSuccess(instances, searchText) {
+    return { instances, searchText }
+  }
+
+  reloadInstancesFailed(response) {
+    return response || true
+  }
+
+  loadInstancesDetails(endpointId, instances) {
+    instances.forEach(instance => {
+      InstanceSource.loadInstanceDetails(endpointId, instance.instance_name).then(
+        instance => { this.loadInstanceDetailsSuccess(instance) },
+        response => { this.loadInstanceDetailsFailed(response) },
+      )
+    })
+
+    return { count: instances.length }
+  }
+
+  loadInstanceDetails(endpointId, instanceName) {
+    InstanceSource.loadInstanceDetails(endpointId, instanceName).then(
+      instance => { this.loadInstanceDetailsSuccess(instance) },
+      response => { this.loadInstanceDetailsFailed(response) },
+    )
+
+    return true
+  }
+
+  loadInstanceDetailsSuccess(instance) {
+    return instance
+  }
+
+  loadInstanceDetailsFailed(response) {
+    return response || true
+  }
+}
+
+export default alt.createActions(InstanceActions)

+ 108 - 0
src/actions/MigrationActions.js

@@ -0,0 +1,108 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import MigrationSource from '../sources/MigrationSource'
+
+class MigrationActions {
+  getMigrations(options) {
+    MigrationSource.getMigrations().then(
+      response => { this.getMigrationsSuccess(response) },
+      response => { this.getMigrationsFailed(response) },
+    )
+    return options || true
+  }
+
+  getMigrationsSuccess(migrations) {
+    return migrations || true
+  }
+
+  getMigrationsFailed(response) {
+    return response || true
+  }
+
+  getMigration(migrationId, showLoading) {
+    MigrationSource.getMigration(migrationId).then(
+      migration => { this.getMigrationSuccess(migration) },
+      response => { this.getMigrationFailed(response) },
+    )
+
+    return { migrationId, showLoading }
+  }
+
+  getMigrationSuccess(migration) {
+    return migration
+  }
+
+  getMigrationFailed(response) {
+    return response || true
+  }
+
+  cancel(migrationId) {
+    MigrationSource.cancel(migrationId).then(
+      () => { this.cancelSuccess(migrationId) },
+      response => { this.cancelFailed(response) },
+    )
+
+    return { migrationId }
+  }
+
+  cancelSuccess(migrationId) {
+    return { migrationId }
+  }
+
+  cancelFailed(response) {
+    return response || true
+  }
+
+  delete(migrationId) {
+    MigrationSource.delete(migrationId).then(
+      () => { this.deleteSuccess(migrationId) },
+      response => { this.deleteFailed(response) },
+    )
+    return migrationId
+  }
+
+  deleteSuccess(migrationId) {
+    return migrationId
+  }
+
+  deleteFailed(response) {
+    return response || true
+  }
+
+  migrateReplica(replicaId, options) {
+    MigrationSource.migrateReplica(replicaId, options).then(
+      migration => { this.migrateReplicaSuccess(migration) },
+      response => { this.migrateReplicaFailed(response) },
+    )
+
+    return { replicaId, options }
+  }
+
+  migrateReplicaSuccess(migration) {
+    return migration
+  }
+
+  migrateReplicaFailed(response) {
+    return response || true
+  }
+
+  clearDetails() {
+    return true
+  }
+}
+
+export default alt.createActions(MigrationActions)

+ 0 - 359
src/actions/MigrationActions/MigrationActions.js

@@ -1,359 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import Reflux from 'reflux';
-import Api from '../../components/ApiCaller';
-import NotificationActions from '../NotificationActions'
-import { servicesUrl, securityGroups } from '../../config';
-
-let MigrationActions = Reflux.createActions({
-  loadMigrations: { children: ['completed', 'failed'], shouldEmit: () => {} },
-  loadReplicas: { children: ['completed', 'failed'], shouldEmit: () => {} },
-  loadMigration: { children: ['completed', 'failed'] }, // TODO: Reload migration action
-  addMigration: { children: ['completed', 'failed'] },
-  deleteMigration: { children: ['completed', 'failed'] },
-  deleteReplica: { children: ['completed', 'failed'] },
-  executeReplica: { children: ['completed', 'failed'] },
-  cancelMigration: { children: ['completed', 'failed'] },
-  getReplicaExecutions: { children: ['completed', 'failed'] },
-  getReplicaExecutionDetail: { children: ['completed', 'failed'] },
-  createMigrationFromReplica: { children: ['completed', 'failed'] },
-  deleteReplicaExecution: { children: ['completed', 'failed'] },
-  getMigration: {},
-  setMigration: {},
-  setReplica: {},
-  setMigrationProperty: {}
-})
-
-MigrationActions.loadMigrations.listen(() => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/migrations/detail`,
-    method: "GET"
-  })
-    .then(MigrationActions.loadMigrations.completed, MigrationActions.loadMigrations.failed)
-    .catch(MigrationActions.loadMigrations.failed);
-})
-
-MigrationActions.loadMigrations.shouldEmit = () => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  let scoped = Reflux.GlobalState.userStore.currentUser.scoped
-  return typeof projectId !== "undefined" && scoped
-}
-
-MigrationActions.loadReplicas.listen(() => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/replicas/detail`,
-    method: "GET"
-  })
-    .then(MigrationActions.loadReplicas.completed, MigrationActions.loadReplicas.failed)
-    .catch(MigrationActions.loadReplicas.failed);
-})
-
-MigrationActions.loadReplicas.shouldEmit = () => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-
-  let scoped = Reflux.GlobalState.userStore.currentUser.scoped
-  return typeof projectId !== "undefined" && scoped
-}
-
-MigrationActions.loadMigration.listen((migration) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/migrations/${migration.id}`,
-    method: "GET"
-  })
-    .then(MigrationActions.loadMigration.completed, MigrationActions.loadMigration.failed)
-    .catch(MigrationActions.loadMigration.failed);
-})
-
-MigrationActions.loadMigration.shouldEmit = () => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  return typeof projectId !== "undefined"
-}
-
-MigrationActions.deleteMigration.listen((migration, callback = null) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/migrations/${migration.id}`,
-    method: "DELETE"
-  })
-  .then(() => {
-    MigrationActions.deleteMigration.completed(migration)
-    if (callback) {
-      callback(migration)
-    }
-  }, MigrationActions.deleteMigration.failed)
-  .catch(MigrationActions.deleteMigration.failed);
-})
-
-MigrationActions.deleteReplica.listen((replica, callback = null) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/replicas/${replica.id}`,
-    method: "DELETE"
-  })
-    .then(() => {
-      MigrationActions.deleteReplica.completed(replica)
-      if (callback) {
-        callback(replica)
-      }
-    }, MigrationActions.deleteReplica.failed)
-    .catch(MigrationActions.deleteReplica.failed);
-})
-
-MigrationActions.executeReplica.listen((replica, callback = null, errorCallback = null, options = null) => {
-  if (replica.type == 'replica') {
-    // check if endpoints exists
-    let connections = Reflux.GlobalState.connectionStore.connections
-    let originEndpoint = false
-    let destinationEndpoint = false
-
-    connections.forEach(connection => {
-      if (replica.origin_endpoint_id === connection.id) {
-        originEndpoint = true
-      }
-      if (replica.destination_endpoint_id === connection.id) {
-        destinationEndpoint = true
-      }
-    })
-
-    if (!originEndpoint) {
-      NotificationActions.notify("Origin endpoint is missing. Cannot execute this replica.", "error")
-      return
-    }
-    if (!destinationEndpoint) {
-      NotificationActions.notify("Destination endpoint is missing. Cannot execute this replica.", "error")
-      return
-    }
-    let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-
-    let payload = { execution: { shutdown_instances: false } }
-    if (options) {
-      options.forEach(o => {
-        payload.execution[o.field] = o.value || false
-      })
-    }
-
-    Api.sendAjaxRequest({
-      url: `${servicesUrl.coriolis}/${projectId}/replicas/${replica.id}/executions`,
-      method: "POST",
-      data: payload
-    })
-      .then((response) => {
-        MigrationActions.executeReplica.completed(replica, response)
-        if (callback) {
-          callback(replica, response)
-        }
-      }, (err) => {
-        MigrationActions.executeReplica.failed(err)
-        if (errorCallback) {
-          errorCallback(replica, err)
-        }
-      })
-      .catch((err) => {
-        MigrationActions.executeReplica.failed(err)
-        if (errorCallback) {
-          errorCallback(replica, err)
-        }
-      });
-  } else {
-    NotificationActions.notify("You cannot execute a migration.", "warning")
-  }
-})
-
-MigrationActions.cancelMigration.listen((migration, callback = null) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  let url = null
-  if (migration.type == 'migration') {
-    url = `${servicesUrl.coriolis}/${projectId}/migrations/${migration.id}/actions`
-  } else {
-    if (migration.executions.length) {
-      let executionId = migration.executions[migration.executions.length - 1].id
-      url = `${servicesUrl.coriolis}/${projectId}/replicas/${migration.id}/executions/${executionId}/actions`
-    } else {
-      NotificationActions.notify("No executions to cancel on this replica")
-    }
-  }
-
-  let payload = {
-    cancel: null
-  }
-
-  Api.sendAjaxRequest({
-    url: url,
-    method: "POST",
-    data: payload
-  })
-    .then((response) => {
-      if (callback) {
-        callback(migration, response)
-      }
-      MigrationActions.cancelMigration.completed(migration, response)
-    }, MigrationActions.cancelMigration.failed)
-    .catch(MigrationActions.cancelMigration.failed);
-})
-
-MigrationActions.getReplicaExecutions.listen((replica, callback) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/replicas/${replica.id}/executions/detail`,
-    method: "GET"
-  })
-    .then((response) => {
-      MigrationActions.getReplicaExecutions.completed(replica, response)
-      if (callback) callback()
-    }, MigrationActions.getReplicaExecutions.failed)
-    .catch(MigrationActions.getReplicaExecutions.failed);
-})
-
-
-MigrationActions.getReplicaExecutionDetail.listen((replica, executionId, callback = null) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/replicas/${replica.id}/executions/${executionId}`,
-    method: "GET"
-  })
-    .then((response) => {
-      MigrationActions.getReplicaExecutionDetail.completed(replica, executionId, response)
-      if (callback) {
-        callback(replica, executionId, response)
-      }
-    }, MigrationActions.getReplicaExecutionDetail.failed)
-    .catch(MigrationActions.getReplicaExecutionDetail.failed);
-})
-
-MigrationActions.deleteReplicaExecution.listen((replica, executionId, callback = null) => {
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/replicas/${replica.id}/executions/${executionId}`,
-    method: "DELETE"
-  })
-    .then((response) => {
-      MigrationActions.deleteReplicaExecution.completed(replica, executionId)
-      if (callback) {
-        callback(replica, executionId, response)
-      }
-    }, MigrationActions.deleteReplicaExecution.failed)
-    .catch(MigrationActions.deleteReplicaExecution.failed);
-})
-
-MigrationActions.addMigration.listen((migration, callback = null, errorCallback = null) => {
-  let payload = {}
-  let instances = []
-
-  migration.selectedInstances.forEach(instance => {
-    if (migration.selectedInstances.indexOf(instance.id)) {
-      instances.push(instance.instance_name)
-    }
-  })
-
-  let networkMap = {}
-  if (migration.networks) {
-    migration.networks.forEach(network => {
-      networkMap[network.network_name] = network.migrateNetwork
-    })
-  }
-
-  let destinationEnv = {}
-
-  let getValueInOriginalDataType = (fieldName, value) => {
-    let fields = migration.targetCloud.cloudRef['import_' + migration.migrationType].fields
-    let field = fields.find(f => f.name === fieldName)
-
-    if (!field) {
-      return value
-    }
-
-    switch (field.dataType) {
-      case 'boolean':
-        return value === 'true' || value === true
-      case 'integer':
-        return parseInt(value, 10)
-      default:
-        return value
-    }
-  }
-
-  for (let i in migration.destination_environment) {
-    let destField = migration.destination_environment[i]
-
-    if (destField.label) { // removing label from dropdown if present
-      destinationEnv[i] = getValueInOriginalDataType(i, destField.value)
-    } else {
-      destinationEnv[i] = getValueInOriginalDataType(i, destField)
-    }
-  }
-
-  destinationEnv.network_map = networkMap
-
-  payload[migration.migrationType] = {
-    origin_endpoint_id: migration.sourceCloud.credential.id,
-    destination_endpoint_id: migration.targetCloud.credential.id,
-    destination_environment: destinationEnv,
-    instances: instances,
-    notes: migration.notes,
-    security_groups: securityGroups
-  }
-
-  let migrationType = migration.migrationType === 'replica' ? 'replicas' : 'migrations'
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/${migrationType}`,
-    method: "POST",
-    data: payload
-  })
-    .then((response) => {
-      MigrationActions.addMigration.completed(response);
-      if (callback) {
-        callback(migration);
-      }
-    }, (response) => {
-      MigrationActions.addMigration.failed(response)
-      if (errorCallback) {
-        errorCallback(migration);
-      }
-    })
-    .catch(MigrationActions.addMigration.failed);
-})
-
-MigrationActions.createMigrationFromReplica.listen((replica, options) => {
-  let payload = {
-    migration: {
-      replica_id: replica.id
-    }
-  }
-  options.forEach(o => {
-    payload.migration[o.field] = o.value || false
-  })
-
-  let projectId = Reflux.GlobalState.userStore.currentUser.project.id
-
-  Api.sendAjaxRequest({
-    url: `${servicesUrl.coriolis}/${projectId}/migrations`,
-    method: "POST",
-    data: payload
-  })
-    .then(MigrationActions.createMigrationFromReplica.completed, MigrationActions.createMigrationFromReplica.failed)
-    .catch(MigrationActions.createMigrationFromReplica.failed);
-})
-
-
-export default MigrationActions;

+ 0 - 6
src/actions/MigrationActions/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "MigrationActions",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./MigrationActions.js"
-}

+ 16 - 39
src/components/ErrorPage/ErrorPage.scss → src/actions/NetworkActions.js

@@ -1,61 +1,38 @@
 /*
 /*
 Copyright (C) 2017  Cloudbase Solutions SRL
 Copyright (C) 2017  Cloudbase Solutions SRL
-
 This program is free software: you can redistribute it and/or modify
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.
 License, or (at your option) any later version.
-
 This program is distributed in the hope that it will be useful,
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.
 GNU Affero General Public License for more details.
-
 You should have received a copy of the GNU Affero General Public License
 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 */
 
 
-* {
-  margin: 0;
-  line-height: 1.2;
-}
-
-html {
-  display: table;
-  width: 100%;
-  height: 100%;
-  color: #888;
-  text-align: center;
-  font-family: sans-serif;
-}
-
-body {
-  display: table-cell;
-  margin: 2em auto;
-  vertical-align: middle;
-}
-
-h1 {
-  color: #555;
-  font-weight: $weight-regular;
-  font-size: 2em;
-}
+import alt from '../alt'
 
 
-p {
-  margin: 0 auto;
-  width: 280px;
-}
+import NetworkSource from '../sources/NetworkSource'
 
 
-@media only screen and (max-width: 280px) {
+class NetworkActions {
+  loadNetworks(endpointId, environment) {
+    NetworkSource.loadNetworks(endpointId, environment).then(
+      networks => { this.loadNetworksSuccess(networks) },
+      response => { this.loadNetworksFailed(response) }
+    )
 
 
-  body, p {
-    width: 95%;
+    return true
   }
   }
 
 
-  h1 {
-    font-size: 1.5em;
-    margin: 0 0 0.3em;
-
+  loadNetworksSuccess(networks) {
+    return networks
   }
   }
 
 
+  loadNetworksFailed(response) {
+    return response || true
+  }
 }
 }
+
+export default alt.createActions(NetworkActions)

+ 7 - 11
src/actions/NotificationActions/NotificationActions.js → src/actions/NotificationActions.js

@@ -1,27 +1,23 @@
 /*
 /*
 Copyright (C) 2017  Cloudbase Solutions SRL
 Copyright (C) 2017  Cloudbase Solutions SRL
-
 This program is free software: you can redistribute it and/or modify
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.
 License, or (at your option) any later version.
-
 This program is distributed in the hope that it will be useful,
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.
 GNU Affero General Public License for more details.
-
 You should have received a copy of the GNU Affero General Public License
 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 */
 
 
-import Reflux from 'reflux';
+import alt from '../alt'
 
 
-let NotificationActions = Reflux.createActions({
-  removeNotification: {},
-  keepNotification: {},
-  markAsRead: {},
-  notify: {}
-})
+class NotificationActions {
+  notify(message, level, options) {
+    return { message, level, ...options }
+  }
+}
 
 
-export default NotificationActions;
+export default alt.createActions(NotificationActions)

+ 0 - 6
src/actions/NotificationActions/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "NotificationActions",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./NotificationActions.js"
-}

+ 38 - 0
src/actions/ProjectActions.js

@@ -0,0 +1,38 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import PojectSource from '../sources/ProjectSource'
+
+class ProjectActions {
+  getProjects() {
+    PojectSource.getProjects().then(
+      this.getProjectsCompleted.bind(this),
+      this.getProjectsFailed.bind(this)
+    ).catch(this.getProjectsFailed.bind(this))
+
+    return true
+  }
+
+  getProjectsCompleted(response) {
+    return response || true
+  }
+
+  getProjectsFailed(response) {
+    return response || true
+  }
+}
+
+export default alt.createActions(ProjectActions)

+ 70 - 0
src/actions/ProviderActions.js

@@ -0,0 +1,70 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import ProviderSource from '../sources/ProviderSource'
+
+class ProviderActions {
+  getConnectionInfoSchema(providerName) {
+    ProviderSource.getConnectionInfoSchema(providerName).then(
+      schema => { this.getConnectionInfoSchemaSuccess(schema) },
+      response => { this.getConnectionInfoSchemaFailed(response) },
+    )
+    return true
+  }
+
+  getConnectionInfoSchemaSuccess(schema) {
+    return schema
+  }
+
+  getConnectionInfoSchemaFailed(response) {
+    return response || true
+  }
+
+  loadProviders() {
+    ProviderSource.loadProviders().then(
+      providers => { this.loadProvidersSuccess(providers) },
+      response => { this.loadProvidersFailed(response) },
+    )
+
+    return true
+  }
+
+  loadProvidersSuccess(providers) {
+    return providers
+  }
+
+  loadProvidersFailed(response) {
+    return response || true
+  }
+
+  loadOptionsSchema(providerName, schemaType) {
+    ProviderSource.loadOptionsSchema(providerName, schemaType).then(
+      schema => { this.loadOptionsSchemaSuccess(schema) },
+      response => { this.loadOptionsSchemaFailed(response) },
+    )
+    return true
+  }
+
+  loadOptionsSchemaSuccess(schema) {
+    return schema
+  }
+
+  loadOptionsSchemaFailed(response) {
+    return response || true
+  }
+}
+
+export default alt.createActions(ProviderActions)

+ 178 - 0
src/actions/ReplicaActions.js

@@ -0,0 +1,178 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import ReplicaSource from '../sources/ReplicaSource'
+
+class ReplicaActions {
+  getReplicas(options) {
+    ReplicaSource.getReplicas().then(
+      response => { this.getReplicasSuccess(response) },
+      response => { this.getReplicasFailed(response) },
+    )
+    return options || true
+  }
+
+  getReplicasSuccess(replicas) {
+    return replicas || true
+  }
+
+  getReplicasFailed(response) {
+    return response || true
+  }
+
+  getReplicasExecutions(replicas) {
+    let count = 0
+    let replicasExecutions = []
+    replicas.forEach(replica => {
+      ReplicaSource.getReplicaExecutions(replica.id).then(
+        response => {
+          count += 1
+          replicasExecutions.push(response)
+
+          if (count === replicas.length) {
+            this.getReplicasExecutionsSuccess(replicasExecutions)
+          }
+        },
+        response => {
+          count += 1
+          if (count === replicas.length) {
+            if (replicasExecutions.length > 0) {
+              this.getReplicasExecutionsSuccess(replicasExecutions)
+            } else {
+              this.getReplicasExecutionsFailed(response)
+            }
+          }
+        },
+      )
+    })
+
+    return replicas
+  }
+
+  getReplicasExecutionsSuccess(replicasExecutions) {
+    return replicasExecutions
+  }
+
+  getReplicasExecutionsFailed(response) {
+    return response || true
+  }
+
+  getReplicaExecutions(replicaId) {
+    ReplicaSource.getReplicaExecutions(replicaId).then(
+      response => { this.getReplicaExecutionsSuccess(response) },
+      response => { this.getReplicaExecutionsFailed(response) },
+    )
+    return replicaId
+  }
+
+  getReplicaExecutionsSuccess({ replicaId, executions }) {
+    return { replicaId, executions }
+  }
+
+  getReplicaExecutionsFailed(response) {
+    return response || true
+  }
+
+  getReplica(replicaId) {
+    ReplicaSource.getReplica(replicaId).then(
+      replica => { this.getReplicaSuccess(replica) },
+      response => { this.getReplicaFailed(response) },
+    )
+
+    return replicaId
+  }
+
+  getReplicaSuccess(replica) {
+    return replica
+  }
+
+  getReplicaFailed(response) {
+    return response || true
+  }
+
+  execute(replicaId, fields) {
+    ReplicaSource.execute(replicaId, fields).then(
+      executions => { this.executeSuccess(executions) },
+      response => { this.executeFailed(response) },
+    )
+
+    return replicaId
+  }
+
+  executeSuccess({ replicaId, execution }) {
+    return { replicaId, execution }
+  }
+
+  executeFailed(response) {
+    return response || true
+  }
+
+  cancelExecution(replicaId, executionId) {
+    ReplicaSource.cancelExecution(replicaId, executionId).then(
+      () => { this.cancelExecutionSuccess(replicaId, executionId) },
+      response => { this.cancelExecutionFailed(response) },
+    )
+
+    return { replicaId, executionId }
+  }
+
+  cancelExecutionSuccess(replicaId, executionId) {
+    return { replicaId, executionId }
+  }
+
+  cancelExecutionFailed(response) {
+    return response || true
+  }
+
+  deleteExecution(replicaId, executionId) {
+    ReplicaSource.deleteExecution(replicaId, executionId).then(
+      () => { this.deleteExecutionSuccess(replicaId, executionId) },
+      response => { this.deleteExecutionFailed(response) },
+    )
+
+    return { replicaId, executionId }
+  }
+
+  deleteExecutionSuccess(replicaId, executionId) {
+    return { replicaId, executionId }
+  }
+
+  deleteExecutionFailed(response) {
+    return response || true
+  }
+
+  delete(replicaId) {
+    ReplicaSource.delete(replicaId).then(
+      () => { this.deleteSuccess(replicaId) },
+      response => { this.deleteFailed(response) },
+    )
+    return replicaId
+  }
+
+  deleteSuccess(replicaId) {
+    return replicaId
+  }
+
+  deleteFailed(response) {
+    return response || true
+  }
+
+  clearDetails() {
+    return true
+  }
+}
+
+export default alt.createActions(ReplicaActions)

+ 105 - 0
src/actions/ScheduleActions.js

@@ -0,0 +1,105 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import ScheduleSource from '../sources/ScheduleSource'
+
+class ScheduleActions {
+  scheduleMultiple(replicaId, schedules) {
+    ScheduleSource.scheduleMultiple(replicaId, schedules).then(
+      s => { this.scheduleMultipleSuccess(s) },
+      response => { this.scheduleMultipleFailed(response) },
+    )
+    return { replicaId, schedules }
+  }
+
+  scheduleMultipleSuccess(schedules) {
+    return schedules
+  }
+
+  scheduleMultipleFailed(response) {
+    return response || true
+  }
+
+  getSchedules(replicaId) {
+    ScheduleSource.getSchedules(replicaId).then(
+      schedules => { this.getSchedulesSuccess(schedules) },
+      response => { this.getSchedulesFailed(response) },
+    )
+
+    return replicaId
+  }
+
+  getSchedulesSuccess(schedules) {
+    return schedules
+  }
+
+  getSchedulesFailed(response) {
+    return response || true
+  }
+
+  addSchedule(replicaId, schedule) {
+    ScheduleSource.addSchedule(replicaId, schedule).then(
+      schedule => { this.addScheduleSuccess(schedule) },
+      response => { this.addScheduleFailed(response) },
+    )
+
+    return replicaId
+  }
+
+  addScheduleSuccess(schedule) {
+    return schedule
+  }
+
+  addScheduleFailed(response) {
+    return response || true
+  }
+
+  removeSchedule(replicaId, scheduleId) {
+    ScheduleSource.removeSchedule(replicaId, scheduleId).then(
+      () => { this.removeScheduleSuccess() },
+      response => { this.removeScheduleFailed(response) },
+    )
+
+    return { replicaId, scheduleId }
+  }
+
+  removeScheduleSuccess() {
+    return true
+  }
+
+  removeScheduleFailed(response) {
+    return response || true
+  }
+
+  updateSchedule(replicaId, scheduleId, data, oldData) {
+    ScheduleSource.updateSchedule(replicaId, scheduleId, data, oldData).then(
+      schedule => { this.updateScheduleSuccess(schedule) },
+      response => { this.updateScheduleFailed(response) },
+    )
+
+    return { replicaId, scheduleId, data }
+  }
+
+  updateScheduleSuccess(schedule) {
+    return schedule
+  }
+
+  updateScheduleFailed(response) {
+    return response || null
+  }
+}
+
+export default alt.createActions(ScheduleActions)

+ 127 - 0
src/actions/UserActions.js

@@ -0,0 +1,127 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import UserSource from '../sources/UserSource'
+import ProjectActions from './ProjectActions'
+import ProjectStore from '../stores/ProjectStore'
+import Wait from '../utils/Wait'
+import NotificationActions from './NotificationActions'
+
+class UserActions {
+  login(data) {
+    UserSource.login(data).then(this.loginSuccess, this.loginFailed)
+    return data
+  }
+
+  loginSuccess() {
+    this.loginScoped()
+    return true
+  }
+
+  loginFailed(response) {
+    return response || true
+  }
+
+  loginScoped(projectId) {
+    let projectStore = ProjectStore.getState()
+    if (projectStore.projects && projectStore.projects.length) {
+      UserSource.loginScoped(projectId || projectStore.projects[0].id)
+        .then(this.loginScopedSuccess, this.loginScopedFailed)
+    } else {
+      ProjectActions.getProjects()
+      Wait.for(() => ProjectStore.getState().projects.length, () => {
+        UserSource.loginScoped(projectId || ProjectStore.getState().projects[0].id)
+          .then(this.loginScopedSuccess, this.loginScopedFailed)
+      })
+    }
+    return projectId || true
+  }
+
+  loginScopedSuccess(response) {
+    this.getUserInfo(response)
+    NotificationActions.notify('Signed in', 'success')
+    return response || true
+  }
+
+  loginScopedFailed(response) {
+    return response || true
+  }
+
+  tokenLogin() {
+    UserSource.tokenLogin().then(this.tokenLoginSuccess, this.tokenLoginFailed)
+    return true
+  }
+
+  tokenLoginSuccess(response) {
+    NotificationActions.notify('Signed in', 'success')
+    this.getUserInfo(response)
+    return response || true
+  }
+
+  tokenLoginFailed(response) {
+    return response || true
+  }
+
+  switchProject(projectId) {
+    NotificationActions.notify('Switching projects')
+    UserSource.switchProject().then(
+      () => { this.switchProjectSuccess(projectId) },
+      response => { this.switchProjectFailed(response) }
+    )
+    return projectId || true
+  }
+
+  switchProjectSuccess(projectId) {
+    this.loginScoped(projectId)
+    return projectId || true
+  }
+
+  switchProjectFailed(response) {
+    this.logout()
+    return response || true
+  }
+
+  logout() {
+    UserSource.logout().then(() => { this.logoutSuccess() }, () => { this.logoutFailed() })
+    return true
+  }
+
+  logoutSuccess() {
+    return true
+  }
+
+  logoutFailed() {
+    return true
+  }
+
+  getUserInfo(user) {
+    UserSource.getUserInfo(user).then(
+      response => { this.getUserInfoSuccess(response) },
+      response => { this.getUserInfoFailed(response) }
+    )
+    return user || true
+  }
+
+  getUserInfoSuccess(response) {
+    return response || true
+  }
+
+  getUserInfoFailed(response) {
+    return response || true
+  }
+}
+
+export default alt.createActions(UserActions)

+ 0 - 171
src/actions/UserActions/UserActions.js

@@ -1,171 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import Reflux from 'reflux';
-import Api from '../../components/ApiCaller';
-import { servicesUrl, defaultDomain } from '../../config';
-
-
-let UserAction = Reflux.createActions({
-  login: { children: ["success", "failed"] },
-  loginGoogle: { children: ["success", "failed"] },
-  loginScope: { children: ["success", "failed"] },
-  logout: {},
-  tokenLogin: { children: ["failed"] },
-  setCurrentUser: {},
-  switchProject: {},
-  getScopedProjects: { children: ["completed", "failed"] },
-  loadProjects: { children: ["completed", "failed"] },
-  federateToken: { },
-  getUserInfo: { children: ["completed", "failed"] },
-  setUserInfo: { children: ["success", "failed"] }
-})
-
-UserAction.login.listen((userData, callback = null) => {
-  let auth = {
-    auth: {
-      identity: {
-        methods: [
-          "password"
-        ],
-        password: {
-          user: {
-            name: userData.name,
-            domain: {
-              name: userData.domain ? userData.domain : defaultDomain
-            },
-            password: userData.password
-          }
-        }
-      },
-      scope: "unscoped"
-    }
-  }
-
-  Api.setDefaultHeader({ "X-Auth-Token": null })
-
-  Api.sendAjaxRequest({
-    url: servicesUrl.identity,
-    method: "POST",
-    data: auth
-  })
-    .then((response) => {
-      UserAction.login.success(response)
-    }, () => {
-      UserAction.login.failed()
-      if (typeof callback == "function") {
-        callback()
-      }
-    })
-})
-
-UserAction.loginScope.listen((token, projectId, fallback = true) => {
-  let auth = {
-    auth: {
-      identity: {
-        methods: [
-          "token"
-        ],
-        token: {
-          id: token
-        }
-      },
-      scope: {
-        project: {
-          id: projectId
-        }
-      }
-    }
-  }
-
-  Api.setDefaultHeader({ "X-Auth-Token": null })
-
-  Api.sendAjaxRequest({
-    url: servicesUrl.identity,
-    method: "POST",
-    data: auth
-  })
-    .then((response) => {
-      UserAction.loginScope.success(response)
-    }, () => {
-      if (fallback) {
-        UserAction.loginScope.failed(token)
-      }
-    })
-})
-
-UserAction.tokenLogin.listen((token) => {
-  Api.sendAjaxRequest({
-    url: servicesUrl.identity,
-    method: "GET",
-    headers: { 'X-Subject-Token': token }
-  })
-  .then(UserAction.login.success, UserAction.tokenLogin.failed)
-  .catch((response) => {
-    UserAction.tokenLogin.failed(response)
-  });
-})
-
-UserAction.getScopedProjects.listen((callback) => {
-  Api.sendAjaxRequest({
-    url: servicesUrl.projects,
-    method: "GET"
-  })
-    .then(
-      (response) => {
-        if (callback) {
-          callback(response)
-        }
-        UserAction.getScopedProjects.completed(response)
-      }, UserAction.getScopedProjects.failed)
-    .catch((response) => {
-      UserAction.getScopedProjects.failed(response)
-    });
-})
-
-UserAction.getUserInfo.listen((userId) => {
-  Api.sendAjaxRequest({
-    url: servicesUrl.users + "/" + userId,
-    method: "GET"
-  })
-    .then(
-      (response) => {
-        UserAction.getUserInfo.completed(response)
-      }, UserAction.getUserInfo.failed)
-    .catch((response) => {
-      UserAction.getUserInfo.failed(response)
-    });
-})
-
-UserAction.setUserInfo.listen((userId, userObject) => {
-  Api.sendAjaxRequest({
-    url: servicesUrl.users + "/" + userId,
-    method: "PATCH",
-    data: {
-      user: userObject
-    }
-  })
-    .then(
-      (response) => {
-        UserAction.setUserInfo.completed(response)
-      }, UserAction.setUserInfo.failed)
-    .catch((response) => {
-      UserAction.setUserInfo.failed(response)
-    });
-})
-
-export default UserAction;

+ 0 - 6
src/actions/UserActions/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "UserActions",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./UserActions.js"
-}

+ 91 - 0
src/actions/WizardActions.js

@@ -0,0 +1,91 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import alt from '../alt'
+
+import WizardSource from '../sources/WizardSource'
+
+class WizardActions {
+  updateData(data) {
+    return data
+  }
+
+  toggleInstanceSelection(instance) {
+    return instance
+  }
+
+  clearData() {
+    return true
+  }
+
+  setCurrentPage(page) {
+    return page
+  }
+
+  updateOptions({ field, value }) {
+    return { field, value }
+  }
+
+  updateNetworks({ sourceNic, targetNetwork }) {
+    return { sourceNic, targetNetwork }
+  }
+
+  addSchedule(schedule) {
+    return schedule || true
+  }
+
+  updateSchedule(scheduleId, data) {
+    return { scheduleId, data }
+  }
+
+  removeSchedule(scheduleId) {
+    return scheduleId
+  }
+
+  create(type, data) {
+    WizardSource.create(type, data).then(
+      item => { this.createSuccess(item) },
+      response => { this.createFailed(response) }
+    )
+
+    return { type, data }
+  }
+
+  createSuccess(item) {
+    return item
+  }
+
+  createFailed(reponse) {
+    return reponse || true
+  }
+
+  createMultiple(type, data) {
+    WizardSource.createMultiple(type, data).then(
+      items => { this.createMultipleSuccess(items) },
+      response => { this.createMultipleFailed(response) }
+    )
+
+    return { type, data }
+  }
+
+  createMultipleSuccess(items) {
+    return items
+  }
+
+  createMultipleFailed(response) {
+    return response || true
+  }
+}
+
+export default alt.createActions(WizardActions)

+ 0 - 22
src/actions/WizardActions/WizardActions.js

@@ -1,22 +0,0 @@
-/*
- Copyright (C) 2017  Cloudbase Solutions SRL
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-import Reflux from 'reflux';
-
-let WizardActions = Reflux.createActions(['updateWizardState', 'newState'])
-
-export default WizardActions;

+ 0 - 6
src/actions/WizardActions/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "WizardActions",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./WizardActions.js"
-}

+ 3 - 5
src/components/TextBox/TextBox.scss → src/alt.js

@@ -1,19 +1,17 @@
 /*
 /*
 Copyright (C) 2017  Cloudbase Solutions SRL
 Copyright (C) 2017  Cloudbase Solutions SRL
-
 This program is free software: you can redistribute it and/or modify
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.
 License, or (at your option) any later version.
-
 This program is distributed in the hope that it will be useful,
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.
 GNU Affero General Public License for more details.
-
 You should have received a copy of the GNU Affero General Public License
 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 */
 
 
-.root { }
-.input { }
+import Alt from 'alt/lib'
+
+export default new Alt()

+ 0 - 122
src/client.js

@@ -1,122 +0,0 @@
-/*
- Copyright (C) 2017  Cloudbase Solutions SRL
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-import 'babel-polyfill';
-import ReactDOM from 'react-dom';
-import FastClick from 'fastclick';
-import Router from './routes';
-import Location from './core/Location';
-import { addEventListener, removeEventListener } from './core/DOMUtils';
-
-let cssContainer = document.getElementById('css');
-const appContainer = document.getElementById('app');
-const context = {
-  insertCss: styles => styles._insertCss(),
-  onSetTitle: value => (document.title = value),
-  onSetMeta: (name, content) => {
-    // Remove and create a new <meta /> tag in order to make it work
-    // with bookmarks in Safari
-    const elements = document.getElementsByTagName('meta');
-    Array.from(elements).forEach((element) => {
-      if (element.getAttribute('name') === name) {
-        element.parentNode.removeChild(element);
-      }
-    });
-    const meta = document.createElement('meta');
-    meta.setAttribute('name', name);
-    meta.setAttribute('content', content);
-    document
-      .getElementsByTagName('head')[0]
-      .appendChild(meta);
-  },
-};
-
-// Google Analytics tracking. Don't send 'pageview' event after the first
-// rendering, as it was already sent by the Html component.
-let trackPageview = () => (trackPageview = () => window.ga('send', 'pageview'));
-
-function render(state) {
-  Router.dispatch(state, (newState, component) => {
-    ReactDOM.render(component, appContainer, () => {
-      // Restore the scroll position if it was saved into the state
-      if (state.scrollY !== undefined) {
-        window.scrollTo(state.scrollX, state.scrollY);
-      } else {
-        window.scrollTo(0, 0);
-      }
-
-      trackPageview();
-
-      // Remove the pre-rendered CSS because it's no longer used
-      // after the React app is launched
-      if (cssContainer) {
-        cssContainer.parentNode.removeChild(cssContainer);
-        cssContainer = null;
-      }
-    });
-  });
-}
-
-function run() {
-  let currentLocation = null;
-  let currentState = null;
-
-  // Make taps on links and buttons work fast on mobiles
-  FastClick.attach(document.body);
-
-  // Re-render the app when window.location changes
-  const unlisten = Location.listen(location => {
-    currentLocation = location;
-    currentState = Object.assign({}, location.state, {
-      path: location.pathname,
-      query: location.query,
-      state: location.state,
-      context,
-    });
-    render(currentState);
-  });
-
-  // Save the page scroll position into the current location's state
-  const supportPageOffset = window.pageXOffset !== undefined;
-  const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
-  const setPageOffset = () => {
-    currentLocation.state = currentLocation.state || Object.create(null);
-    if (supportPageOffset) {
-      currentLocation.state.scrollX = window.pageXOffset;
-      currentLocation.state.scrollY = window.pageYOffset;
-    } else {
-      currentLocation.state.scrollX = isCSS1Compat ?
-        document.documentElement.scrollLeft : document.body.scrollLeft;
-      currentLocation.state.scrollY = isCSS1Compat ?
-        document.documentElement.scrollTop : document.body.scrollTop;
-    }
-  };
-
-  addEventListener(window, 'scroll', setPageOffset);
-  addEventListener(window, 'pagehide', () => {
-    removeEventListener(window, 'scroll', setPageOffset);
-    unlisten();
-  });
-}
-
-// Run the application when both DOM is ready and page content is loaded
-if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) {
-  run();
-} else {
-  document.addEventListener('DOMContentLoaded', run, false);
-}

+ 0 - 789
src/components/AddCloudConnection/AddCloudConnection.js

@@ -1,789 +0,0 @@
-/*
- Copyright (C) 2017  Cloudbase Solutions SRL
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/* eslint-disable dot-notation */
-
-import React, { PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './AddCloudConnection.scss';
-import Reflux from 'reflux';
-import Helper from "../Helper"
-import ConnectionsStore from '../../stores/ConnectionsStore';
-import ConnectionsActions from '../../actions/ConnectionsActions';
-import NotificationActions from '../../actions/NotificationActions';
-import Dropdown from '../NewDropdown';
-import Switch from '../Switch'
-import LoadingIcon from "../LoadingIcon/LoadingIcon";
-
-const title = 'Add Cloud Connection';
-const endpointStatuses = { IDLE: 0, VALIDATING: 1, ERROR: 2, SUCCESS: 3 }
-const submissionTypes = { ADD: 0, EDIT: 1 }
-
-class AddCloudConnection extends Reflux.Component {
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  static defaultProps = {
-    cloud: null,
-    connection: null,
-    type: "new",
-    onResizeUpdate: () => {}
-  }
-
-  constructor(props) {
-    super(props)
-
-    this.store = ConnectionsStore
-
-    this.state = {
-      submissionType: submissionTypes.ADD,
-      endpointStatus: endpointStatuses.IDLE,
-      showErrorMessage: false,
-      type: props.type, // type of operation: new/edit
-      connection: props.connection, // connection object (on edit)
-      connectionName: "", // connection name field
-      description: "", // connection description field
-      currentCloud: this.props.cloud, // chosen cloud - if adding a new endpoint
-      currentCloudData: null, // endpoint field data
-      requiredFields: [], // array that holds all the endpoint required fields - used for field validation
-      cloudFormsSubmitted: false // flag that indicates if the form has been submitted - used for field validation
-    }
-  }
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-    this.componentWillUnmount = false
-    this.context.onSetTitle(title);
-    if (this.state.currentCloudData == null) {
-      this.setState({ currentCloudData: {} })
-    }
-
-    this.setState({ submissionType: this.props.type === 'new' ? submissionTypes.ADD : submissionTypes.EDIT })
-  }
-
-  componentWillUnmount() {
-    super.componentWillUnmount.call(this)
-    this.componentWillUnmount = true
-    clearTimeout(this.closeTimeout)
-  }
-
-  componentDidMount() {
-    if (this.state.connection) {
-      this.state.allClouds.forEach(item => {
-        if (item.name === this.state.connection.type) {
-          let credentials = this.state.connection.credentials
-          let newCredentials = {}
-          for (let i in credentials) {
-            if (typeof credentials[i] == "object") {
-              newCredentials['login_type'] = i
-              // credentials['user_credentials'] = {}
-              for (let j in credentials[i]) {
-                // credentials['user_credentials'][j] = credentials[i][j]
-                newCredentials[j] = credentials[i][j]
-              }
-            } else if (typeof credentials[i] === 'boolean') {
-              newCredentials[i] = credentials[i]
-            } else {
-              newCredentials[i] = credentials[i] + ""
-            }
-          }
-          this.setState({
-            currentCloudData: newCredentials,
-            connectionName: this.state.connection.name,
-            description: this.state.connection.description
-          }, () => {
-            this.chooseCloud(item)
-          })
-        }
-      })
-    } else if (this.props.cloud) {
-      this.chooseCloud(this.props.cloud)
-    }
-
-    // Fixes an issue with focus when multiple modals are rendered and escape key is not captured.
-    // Test with adding cloud connection from wizard.
-    setTimeout(() => { this.rootDiv.focus() }, 100)
-  }
-
-  /**
-   * Function called upon saving an endpoint - handles both new and edit operations
-   */
-  handleSave() {
-    let valid = true
-    let requiredFields = this.state.requiredFields
-
-    for (let i in this.state.currentCloudData) {
-      if (requiredFields.indexOf(i) > -1 && !this.state.currentCloudData[i]) {
-        valid = false
-      }
-    }
-    requiredFields.forEach((field) => {
-      if (!this.state.currentCloudData[field]) {
-        valid = false
-      }
-    })
-
-    if (this.state.connectionName.trim().length == 0) {
-      valid = false
-    }
-
-    if (!valid) {
-      NotificationActions.notify("Please fill all required fields", "error")
-      this.setState({ cloudFormsSubmitted: true })
-    } else {
-      let credentials = Object.assign({}, this.state.currentCloudData)
-      for (let key in credentials) {
-        if (credentials[key].label) {
-          credentials[key] = credentials[key].value
-        }
-
-        let field = this.state.currentCloud.endpoint.fields.find(function findByName(f) { return f.name == this }, key);
-
-        if (!field || !field.dataType) {
-          continue;
-        }
-
-        // Convert datatype
-        switch (field.dataType) {
-          case 'boolean':
-            credentials[key] = (credentials[key] === true ||
-              ((typeof credentials[key] === 'string' || credentials[key] instanceof String) &&
-               credentials[key].toLowerCase() == "true"));
-            break;
-          case 'integer':
-            let value = parseInt(credentials[key], 10);
-            if (value.toString() != credentials[key]) {
-              valid = false;
-              NotificationActions.notify('"' + key + '" needs to be an integer', 'error');
-            }
-            credentials[key] = value;
-            break;
-          default:
-            // retain original value
-            break;
-        }
-      }
-
-      if (!valid) {
-        return;
-      }
-
-      // If there's a switch radio, create a hierarchical structure with the selected radio as the root.
-      this.state.currentCloud.endpoint.fields.forEach(field => {
-        if (field.type === 'switch-radio') {
-          credentials[credentials[field.name]] = {}
-
-          field.options.forEach(fieldOptions => {
-            if (fieldOptions.value === credentials[field.name]) {
-              fieldOptions.fields.forEach(fieldOptionField => {
-                credentials[credentials[field.name]][fieldOptionField.name] = credentials[fieldOptionField.name]
-              })
-            }
-          })
-        }
-      })
-
-      // If endpoint is new
-      if (this.state.type === 'new') {
-        this.setState({ type: 'edit' })
-
-        ConnectionsActions.newEndpoint({
-          name: this.state.connectionName,
-          description: this.state.description,
-          type: this.state.currentCloud.name,
-          connection_info: credentials
-        }, (response) => {
-          this.validateEndpoint(response.data.endpoint)
-
-          if (this.props.onConnectionAdded) {
-            this.props.onConnectionAdded(response.data.endpoint)
-          }
-        })
-
-        this.setState({ endpointStatus: endpointStatuses.VALIDATING })
-      } else { // If editing an endpoint
-        ConnectionsActions.editEndpoint(this.state.connection, {
-          name: this.state.connectionName,
-          description: this.state.description,
-          connection_info: credentials
-        }, (response) => {
-          this.validateEndpoint(response.data.endpoint)
-
-          if (this.props.onConnectionAdded && this.submissionType === submissionTypes.ADD) {
-            this.props.onConnectionAdded(response.data.endpoint)
-          }
-        })
-
-        this.setState({ endpointStatus: endpointStatuses.VALIDATING })
-      }
-    }
-  }
-
-  validateEndpoint(endpoint) {
-    if (this.componentWillUnmount && this.state.submissionType === submissionTypes.ADD) {
-      ConnectionsActions.deleteConnection(endpoint)
-      return
-    }
-
-    this.setState({ connection: endpoint })
-
-    ConnectionsActions.validateConnection(endpoint, response => {
-      let validation = response.data["validate-connection"]
-      if (validation.valid) {
-        this.setState({ endpointStatus: endpointStatuses.SUCCESS })
-      } else {
-        this.setState({
-          endpointStatus: endpointStatuses.ERROR,
-          errorMessage: validation.message
-        })
-      }
-    }, () => {
-      this.setState({ endpointStatus: endpointStatuses.ERROR })
-    })
-  }
-
-  /**
-   * Handles change `name` property
-   * @param e
-   */
-  handleChangeName(e) {
-    this.setState({ connectionName: e.target.value })
-  }
-
-  /**
-   * Handles change `description` property
-   * @param e
-   */
-  handleChangeDescription(e) {
-    this.setState({ description: e.target.value })
-  }
-
-  /**
-   * Handler to choose the cloud which the endpoint will be assigned to
-   * @param cloud
-   */
-  chooseCloud(cloud) {
-    let currentCloudData = {}
-    if (this.state.currentCloudData !== null) {
-      currentCloudData = this.state.currentCloudData
-    }
-    let requiredFields = []
-
-    cloud.endpoint.fields.forEach(field => {
-      if (typeof currentCloudData[field.name] == "undefined") {
-        if (typeof field.defaultValue === 'undefined') {
-          currentCloudData[field.name] = "";
-        } else {
-          currentCloudData[field.name] = field.defaultValue.toString();
-        }
-      }
-      if (field.required) {
-        requiredFields.push(field.name)
-      }
-    })
-
-    this.props.onResizeUpdate()
-    this.setState({
-      currentCloud: cloud,
-      currentCloudData: currentCloudData,
-      requiredFields: requiredFields
-    }, this.setDefaultValues)
-  }
-
-  /**
-   * Function that goes back from endpoint validation to edit mode
-   */
-  backToEdit() {
-    this.props.onResizeUpdate()
-    this.setState({ validateEndpoint: null })
-  }
-
-  /**
-   * Handles back operation when adding a new endpoint and want to switch cloud. Resets all previous cloud data.
-   */
-  handleBack() {
-    this.props.onResizeUpdate()
-    this.setState({
-      currentCloudData: null,
-      currentCloud: null,
-      requiredFields: null,
-      connectionName: "",
-      description: null
-    })
-  }
-
-  /**
-   * Handles cancel edit/add endpoint
-   */
-  handleCancel() {
-    if (this.state.submissionType === submissionTypes.ADD && this.state.connection && this.state.connection.id) {
-      ConnectionsActions.deleteConnection(this.state.connection)
-    }
-
-    this.props.closeHandle();
-  }
-
-  handleClose() {
-    this.props.closeHandle();
-  }
-
-  /**
-   * Sets default values for cloud fields
-   */
-  setDefaultValues() {
-    this.state.currentCloud.endpoint.fields.forEach(field => {
-      let currentCloudData = this.state.currentCloudData
-      switch (field.type) {
-        case 'switch':
-          if (field.default && typeof currentCloudData[field.name] == "undefined") {
-            currentCloudData[field.name] = field.default
-            this.setState({ currentCloudData: currentCloudData })
-          }
-          break
-        case 'switch-radio':
-          field.options.forEach(option => {
-            if (option.default && !currentCloudData[field.name]) {
-              currentCloudData[field.name] = option.value
-              this.setRadioRequiredFields(field, option.value)
-              this.setState({ currentCloudData: currentCloudData })
-            }
-          }, this)
-          break;
-        case 'text':
-          if (field.default && typeof currentCloudData[field.name] == "undefined") {
-            currentCloudData[field.name] = field.default
-            this.setState({ currentCloudData: currentCloudData })
-          }
-          break
-        default:
-          break;
-      }
-    }, this)
-  }
-
-  /**
-   * Checks whether the field is valid. Only goes through validation if field is required
-   * @param field
-   * @returns {boolean}
-   */
-  isValid(field) {
-    if (field.required && this.state.cloudFormsSubmitted) {
-      if (this.state.currentCloudData[field.name]) {
-        return !(this.state.currentCloudData[field.name] && this.state.currentCloudData[field.name].length == 0);
-      } else {
-        return false
-      }
-    } else {
-      return true
-    }
-  }
-
-  /**
-   * Dinamically change the required fields affected by the current radio selection
-   * @param field
-   * @param currentValue
-   */
-  setRadioRequiredFields(field, currentValue) {
-    let requiredFields = this.state.requiredFields || [];
-
-    // Remove fields set by previous radio change
-    field.options.forEach(option => {
-      option.fields.forEach(f => {
-        requiredFields = requiredFields.filter(rf => rf !== f.name)
-      })
-    })
-
-    field.options.forEach(option => {
-      if (option.value === currentValue) {
-        option.fields.forEach(optionField => {
-          if (optionField.required) {
-            requiredFields.push(optionField.name);
-          }
-        })
-      }
-    })
-
-    this.setState({ requiredFields: requiredFields });
-  }
-
-  areFieldsDisabled() {
-    return (this.state.endpointStatus === endpointStatuses.VALIDATING
-      || this.state.endpointStatus === endpointStatuses.SUCCESS)
-  }
-
-  /**
-   * Handler to change the endpoint field
-   * @param e
-   * @param field
-   */
-  handleCloudFieldChange(e, field) {
-    let currentCloudData = this.state.currentCloudData
-    if (field.type == 'dropdown') {
-      currentCloudData[field.name] = e.value
-    } else if (field.type === 'switch') {
-      currentCloudData[field.name] = e.target.checked
-    } else {
-      currentCloudData[field.name] = e.target.value
-    }
-
-    if (field.type === 'switch-radio') {
-      this.setRadioRequiredFields(field, e.target.value)
-    }
-
-    this.setState({ currentCloudData: currentCloudData })
-  }
-
-  handleCopyErrorClick() {
-    let succesful = Helper.copyTextToClipboard(this.state.errorMessage)
-
-    if (succesful) {
-      NotificationActions.notify('The error message has been copied to clipboard.')
-    } else {
-      NotificationActions.notify('The error message couldn\'t be copied', 'error')
-    }
-  }
-
-  handleShowErrorClick() {
-    this.setState({
-      showErrorMessage: !this.state.showErrorMessage
-    })
-  }
-
-  /**
-   * Renders the cloud list
-   * @returns {XML}
-   */
-  renderCloudList() {
-    let clouds = this.state.allClouds.map((cloud, index) => {
-      let colorType = ""
-      if (cloud.credentials != null && cloud.credentials.length != 0) {
-        colorType = ""
-      }
-
-      return (
-        <div className={s.cloudContainer} key={"cloudImage_" + index}>
-          <div
-            className={s.cloudImage + " icon large-cloud " + cloud.name + " " + colorType}
-            onClick={() => this.chooseCloud(cloud)}
-          ></div>
-        </div>
-      )
-    }, this)
-
-    return (
-      <div className={s.container}>
-        <div className={s.cloudList}>
-          {clouds}
-        </div>
-        <div className={s.buttons}>
-          <button className={s.centerBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
-        </div>
-      </div>
-    )
-  }
-
-  /**
-   * Renders individual cloud fields
-   * @param field
-   * @returns {XML}
-   */
-  renderField(field) {
-    let returnValue
-    switch (field.type) {
-      case "text":
-        returnValue = (
-          <div className={"form-group " + (this.isValid(field) ? "" : s.error) + (field.required ? ' required' : '')}
-            key={"cloudField_" + field.name}
-          >
-            <div className="input-label">{field.label}</div>
-            <input
-              type="text"
-              placeholder={field.label}
-              disabled={this.areFieldsDisabled()}
-              onChange={(e) => this.handleCloudFieldChange(e, field)}
-              value={this.state.currentCloudData[field.name] || ''}
-            />
-          </div>
-        )
-        break;
-      case "password":
-        returnValue = (
-          <div className={"form-group " + (this.isValid(field) ? "" : s.error) + (field.required ? ' required' : '')}
-            key={"cloudField_" + field.name}
-          >
-            <div className="input-label">{field.label}</div>
-            <input
-              type="password"
-              placeholder={field.label}
-              disabled={this.areFieldsDisabled()}
-              onChange={(e) => this.handleCloudFieldChange(e, field)}
-              value={this.state.currentCloudData[field.name]}
-            />
-          </div>
-        )
-        break;
-      case 'switch':
-        returnValue = (
-          <div
-            className="form-group"
-            key={"cloudField_" + field.name}
-          >
-            <div className="input-label">
-              {field.label + (field.required ? " *" : "")}
-            </div>
-            <Switch
-              className={s.switchButton}
-              labelClassName={s.switchLabel}
-              checked={this.state.currentCloudData[field.name] === true}
-              onChange={(e) => this.handleCloudFieldChange(e, field)}
-              checkedLabel="Yes"
-              uncheckedLabel="No"
-            />
-          </div>
-        )
-        break
-      case "dropdown":
-        returnValue = (
-          <div className={"form-group " + (this.isValid(field) ? "" : s.error)} key={"cloudField_" + field.name}>
-            <div className="input-label">
-              {field.label + (field.required ? " *" : "")}
-            </div>
-            <Dropdown
-              disabled={this.areFieldsDisabled()}
-              options={field.options}
-              onChange={(e) => this.handleCloudFieldChange(e, field)}
-              placeholder="Choose a value"
-              value={field.options.find(function findOption(option) { return option.value == this},
-                     this.state.currentCloudData[field.name])}
-            />
-          </div>
-        )
-        break;
-      case "switch-radio":
-        let fields = ""
-        field.options.forEach((option) => {
-          if (option.value == this.state.currentCloudData[field.name]) {
-            fields = option.fields.map((optionField) => this.renderField(optionField))
-          }
-        })
-        let radioOptions = field.options.map((option, key) => (
-            <div key={"radio_option_" + key} className={s.radioOption}>
-            <input
-              disabled={this.areFieldsDisabled()}
-              type="radio"
-              value={option.value}
-              id={option.name}
-              checked={option.value == this.state.currentCloudData[field.name]}
-              onChange={(e) => this.handleCloudFieldChange(e, field)}
-            /> <label htmlFor={option.name}>{option.label}</label>
-            </div>
-          )
-        )
-        returnValue = (
-          <div key={"cloudField_" + field.name}>
-            <div className="form-group switch-radio" key={"cloudField_" + field.name}>
-              { radioOptions }
-            </div>
-            <div></div>
-            <div className={s.cloudFields + ' ' + s.radioFields}>
-              {fields}
-            </div>
-          </div>
-        )
-        break;
-      default:
-        break
-    }
-    return returnValue
-  }
-
-  renderEndpointErrorMessage() {
-    if (this.state.endpointStatus !== endpointStatuses.ERROR || !this.state.showErrorMessage) {
-      return null
-    }
-
-    return (
-      <div className={s.endpointErrorMessage}
-        onClick={() => this.handleCopyErrorClick()}
-        onMouseDown={e => e.stopPropagation()}
-        onMouseUp={e => e.stopPropagation()}
-      >
-        <span className={s.endpointErrorMessageContent}>
-          {this.state.errorMessage}<span className="copyButton" />
-        </span>
-      </div>
-    )
-  }
-
-  renderEndpointErrorTitle() {
-    let errorMessage = null
-    if (this.state.errorMessage) {
-      errorMessage = (
-        <span className={s.ednpointErrorMessageViewMore}
-          onClick={() => { this.handleShowErrorClick() }}
-        >{this.state.showErrorMessage ? 'Hide Error' : 'Show Error'}</span>
-      )
-    }
-
-    return (
-      <div className={s.endpointErrorMessageTitle}>
-        <span className={s.endpointErrorMessageTitleContent}>Validation Failed</span>
-        {errorMessage}
-      </div>
-    )
-  }
-
-  renderEndpointStatus() {
-    if (this.state.endpointStatus === endpointStatuses.SUCCESS) {
-      clearTimeout(this.closeTimeout)
-      this.closeTimeout = setTimeout(() => {
-        this.closeTimeout = null
-        this.handleClose()
-      }, 2000)
-    }
-
-    let endpointStatus = null
-    if (this.state.endpointStatus === endpointStatuses.ERROR ||
-      this.state.endpointStatus === endpointStatuses.SUCCESS) {
-      let icon = 'successIcon'
-      let content = 'Endpoint is Valid'
-      if (this.state.endpointStatus === endpointStatuses.ERROR) {
-        icon = 'errorIcon'
-        content = this.renderEndpointErrorTitle()
-      }
-
-      endpointStatus = (
-        <div className={s.endpointStatus}>
-          <div className={s.endpointStatusTitle}>
-            <div className={s.endpointStatusIcon + ' ' + icon}></div>
-            <div className={s.endpointStatusLabel}>
-              {content}
-            </div>
-          </div>
-          {this.renderEndpointErrorMessage()}
-        </div>
-      )
-    }
-
-    return endpointStatus
-  }
-
-  renderButtons() {
-    let cancelButton = (this.state.type == "new" && this.props.cloud == null) ?
-      <button className={s.leftBtn + " gray"} onClick={(e) => this.handleBack(e)}>Back</button> :
-      <button className={s.leftBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
-
-    let saveButtonContent = 'Validate and Save'
-
-    if (this.state.endpointStatus === endpointStatuses.VALIDATING ||
-      this.state.endpointStatus === endpointStatuses.SUCCESS) {
-      let text = this.state.endpointStatus === endpointStatuses.VALIDATING ? 'Validating' : 'Saving'
-      saveButtonContent = <span>{text} ... <div className="spinner"></div></span>
-    }
-
-    let saveButton = (
-      <button
-        className={s.rightBtn}
-        onClick={this.handleSave.bind(this)}
-        disabled={this.areFieldsDisabled()}
-      >
-        {saveButtonContent}
-      </button>
-    )
-
-    return (
-      <div className={s.buttons}>
-        {cancelButton}
-        {saveButton}
-      </div>
-    )
-  }
-
-  /**
-   * Renders the new/edit endpoint form
-   * @param cloud
-   * @returns {XML}
-   */
-  renderCloudFields(cloud) {
-    let fields = cloud.endpoint.fields.map(field => this.renderField(field), this)
-
-    return (
-      <div className={s.container}>
-        <div className={s.cloudImage}>
-          <div className={" icon large-cloud " + this.state.currentCloud.name}></div>
-        </div>
-        {this.renderEndpointStatus()}
-        <div className={s.cloudFields + (cloud.endpoint.fields.length > 6 ? " " + s.larger : "")}>
-          <div className={"form-group " + (this.state.cloudFormsSubmitted &&
-            this.state.connectionName.trim().length == 0 ? s.error : "") + ' required'}
-          >
-            <div className="input-label">
-              Endpoint Name
-            </div>
-            <input
-              type="text"
-              placeholder="Endpoint Name"
-              disabled={this.areFieldsDisabled()}
-              onChange={(e) => this.handleChangeName(e)}
-              value={this.state.connectionName}
-            />
-          </div>
-          <div className="form-group">
-            <div className="input-label">
-              Endpoint Description
-            </div>
-            <input
-              type="text"
-              disabled={this.areFieldsDisabled()}
-              placeholder="Endpoint Description"
-              onChange={(e) => this.handleChangeDescription(e)}
-              value={this.state.description}
-            ></input>
-          </div>
-
-          {fields}
-        </div>
-        {this.renderButtons()}
-      </div>
-    )
-  }
-
-  render() {
-    let modalBody
-    if (this.state.currentCloud == null) {
-      if (this.state.allClouds) {
-        modalBody = this.renderCloudList()
-      } else {
-        modalBody = <LoadingIcon />
-      }
-    } else {
-      modalBody = this.renderCloudFields(this.state.currentCloud)
-    }
-    return (
-      <div tabIndex="0" className={s.root} ref={rootDiv => { this.rootDiv = rootDiv }}>
-        <div className={s.header}>
-          <h3>{this.props.type === 'edit' ? 'Edit Cloud Connection' : title}</h3>
-        </div>
-        {modalBody}
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(AddCloudConnection, s);

Plik diff jest za duży
+ 0 - 234
src/components/AddCloudConnection/AddCloudConnection.scss


+ 0 - 6
src/components/AddCloudConnection/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "AddCloudConnection",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./AddCloudConnection.js"
-}

+ 0 - 6
src/components/ApiCaller/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "ApiCaller",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./ApiCaller.js"
-}

+ 79 - 0
src/components/App.jsx

@@ -0,0 +1,79 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import React from 'react'
+import { Switch, Route } from 'react-router-dom'
+import styled, { injectGlobal } from 'styled-components'
+
+import {
+  LoginPage,
+  Fonts,
+  Notifications,
+  NotFoundPage,
+  ReplicasPage,
+  ReplicaDetailsPage,
+  MigrationsPage,
+  MigrationDetailsPage,
+  EndpointsPage,
+  EndpointDetailsPage,
+  WizardPage,
+} from 'components'
+
+import Palette from './styleUtils/Palette'
+import StyleProps from './styleUtils/StyleProps'
+import UserActions from '../actions/UserActions'
+
+injectGlobal`
+  ${Fonts}
+  body {
+    margin: 0;
+    color: ${Palette.black};
+    font-family: Rubik;
+    font-size: 14px;
+    font-weight: ${StyleProps.fontWeights.regular};
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+`
+const Wrapper = styled.div``
+
+class App extends React.Component {
+  componentWillMount() {
+    UserActions.tokenLogin()
+  }
+
+  render() {
+    return (
+      <Wrapper>
+        <Switch>
+          <Route path="/" component={LoginPage} exact />
+          <Route path="/login" component={LoginPage} />
+          <Route path="/replicas" component={ReplicasPage} />
+          <Route path="/replica/:id" component={ReplicaDetailsPage} exact />
+          <Route path="/replica/:page/:id" component={ReplicaDetailsPage} />
+          <Route path="/migrations" component={MigrationsPage} />
+          <Route path="/migration/:id" component={MigrationDetailsPage} exact />
+          <Route path="/migration/:page/:id" component={MigrationDetailsPage} />
+          <Route path="/endpoints" component={EndpointsPage} />
+          <Route path="/endpoint/:id" component={EndpointDetailsPage} />
+          <Route path="/wizard/:type" component={WizardPage} />
+          <Route component={NotFoundPage} />
+        </Switch>
+        <Notifications />
+      </Wrapper>
+    )
+  }
+}
+
+export default App

+ 0 - 109
src/components/App/App.js

@@ -1,109 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { PropTypes } from 'react';
-import Reflux from 'reflux';
-import emptyFunction from 'fbjs/lib/emptyFunction';
-import s from './App.scss';
-import UserStore from '../../stores/UserStore';
-import NotificationsStore from '../../stores/NotificationsStore';
-import ConnectionsStore from '../../stores/ConnectionsStore';
-import NotificationActions from '../../actions/NotificationActions';
-import MigrationStore from '../../stores/MigrationStore';
-import Notifications from '../Notifications'
-import Api from '../ApiCaller'
-import cookie from 'react-cookie'
-import Location from '../../core/Location'
-
-class App extends Reflux.Component {
-
-  static propTypes = {
-    context: PropTypes.shape({
-      insertCss: PropTypes.func,
-      onSetTitle: PropTypes.func,
-      onSetMeta: PropTypes.func,
-      onPageNotFound: PropTypes.func,
-      onNewMigration: PropTypes.func,
-      notify: PropTypes.func
-    }),
-    children: PropTypes.element.isRequired,
-    error: PropTypes.object
-  };
-
-  static childContextTypes = {
-    insertCss: PropTypes.func.isRequired,
-    onSetTitle: PropTypes.func.isRequired,
-    onSetMeta: PropTypes.func.isRequired,
-    onPageNotFound: PropTypes.func.isRequired,
-    notify: PropTypes.func.isRequired
-  };
-
-  constructor(props) {
-    super(props)
-    this.stores = [UserStore, NotificationsStore, ConnectionsStore, MigrationStore]
-    this.state = {
-      notifications: []
-    }
-
-    // init token if page is refreshed
-    let token = cookie.load('token')
-    if (token) {
-      Api.setDefaultHeader('X-Auth-Token', token)
-    } else {
-      Location.push("/login")
-    }
-  }
-
-  getChildContext() {
-    const context = this.props.context;
-    return {
-      insertCss: context.insertCss || emptyFunction,
-      onSetTitle: context.onSetTitle || emptyFunction,
-      onSetMeta: context.onSetMeta || emptyFunction,
-      onPageNotFound: context.onPageNotFound || emptyFunction,
-      notify: this.notify
-    };
-  }
-
-  notify(message, type = "info", title = null) {
-    NotificationActions.notify(message, type, title)
-  }
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-    const { insertCss } = this.props.context;
-    this.removeCss = insertCss(s);
-  }
-
-  componentWillUnmount() {
-    this.removeCss()
-  }
-
-  render() {
-    return !this.props.error ? (
-      <div className={s.root}>
-        <Notifications notifications={this.state.notifications} />
-        {this.props.children}
-      </div>
-    ) : (<div className={s.root}>
-      {this.props.children}
-    </div>)
-  }
-
-}
-
-export default App;

Plik diff jest za duży
+ 0 - 580
src/components/App/App.scss


+ 0 - 6
src/components/App/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "App",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./App.js"
-}

+ 0 - 72
src/components/CloudConnection/CloudConnection.js

@@ -1,72 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { PropTypes } from 'react';
-import Reflux from 'reflux';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './CloudConnection.scss';
-import ConnectionsStore from '../../stores/ConnectionsStore';
-import ConnectionsActions from '../../actions/ConnectionsActions';
-
-
-class CloudConnection extends Reflux.Component {
-  title = ""
-  constructor(props) {
-    super(props)
-    this.store = ConnectionsStore
-
-    this.state = {
-      connection: {
-        name: null,
-        cloudName: null,
-        id: null
-      },
-      title: 'Edit'
-    }
-  }
-
-  static propTypes = {
-    type: PropTypes.string,
-
-  }
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-    ConnectionsActions.loadConnectionDetail(this.props.connectionId)
-  }
-
-  componentDidMount() {
-    this.context.onSetTitle(this.title);
-  }
-
-  render() {
-    return (
-      <div className={s.root}>
-        {React.cloneElement(this.props.children, {
-          connections: this.state.connections,
-          connectionId: this.props.connectionId })}
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(CloudConnection, s);

+ 0 - 6
src/components/CloudConnection/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "CloudConnection",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./CloudConnection.js"
-}

+ 0 - 98
src/components/CloudConnectionAuth/CloudConnectionAuth.js

@@ -1,98 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './CloudConnectionAuth.scss';
-import Helper from '../Helper';
-import LoadingIcon from "../LoadingIcon/LoadingIcon";
-
-
-const title = 'connection details';
-
-class CloudConnectionAuth extends Component {
-  static propTypes = {
-    connection: PropTypes.object
-  }
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  constructor(props) {
-    super(props)
-
-    let fields = this.processProps(props)
-    this.state = { fields: fields }
-  }
-
-  componentWillMount() {
-    this.context.onSetTitle(title);
-  }
-
-  processProps(props) {
-    let fields = []
-    if (props.connection.credentials) {
-      for (let fieldName in props.connection.credentials) {
-        let value = props.connection.credentials[fieldName]
-        if (value.value) { // if dropdown
-          value = value.value
-        }
-        if (value === true) value = "Yes"
-        if (value === false) value = "No"
-
-        fields.push({
-          fieldName: Helper.convertCloudFieldLabel(fieldName),
-          fieldValue: value
-        })
-      }
-    }
-    return fields
-  }
-
-  renderFields() {
-    if (this.state.fields.length) {
-      return this.state.fields.map((field, index) => (
-          <div className={s.formGroup} key={"formGroup_" + index}>
-            <div className={s.title}>
-              {field.fieldName}
-            </div>
-            <div className={s.value}>
-              {field.fieldValue ? field.fieldValue : "-"}
-            </div>
-          </div>
-        )
-      )
-    } else {
-      return <LoadingIcon />
-    }
-  }
-
-  render() {
-    return (
-      <div className={s.root}>
-        <div className={s.container}>
-          {this.renderFields()}
-        </div>
-        <button className="wire">Change Authentication</button>
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(CloudConnectionAuth, s);

+ 0 - 71
src/components/CloudConnectionAuth/CloudConnectionAuth.scss

@@ -1,71 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import '../variables.scss';
-
-.root {
-  :global(.arrow) {
-    width: 36px;
-    height: 28px;
-    margin-top: 30px;
-    margin-left: 130px;
-    position: absolute;
-  }
-}
-
-.container {
-  margin: 0 auto;
-  &:after {
-    clear: both;
-    height: 0;
-    display: block;
-    content: ' ';
-  }
-}
-.columnLeft, .columnRight {
-  width: 50%;
-  float: left;
-}
-.formGroup {
-  margin-bottom: 32px;
-  width: 50%;
-  float: left;
-  .title {
-    font-weight: $weight-semibold;
-    font-size: 10px;
-    color: $gray-dark;
-    text-transform: uppercase;
-    margin-bottom: 8px;
-  }
-  .titleIp {
-    font-weight: $weight-semibold;
-    font-size: 14px;
-    color: $gray;
-  }
-  .value {
-    font-weight: $weight-regular;
-    font-size: 14px;
-    color: $black;
-    a {
-      color: $blue;
-    }
-  }
-}
-.cloudImg {
-  width: 96px;
-  margin-top: 16px;
-}

+ 0 - 6
src/components/CloudConnectionAuth/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "CloudConnectionAuth",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./CloudConnectionAuth.js"
-}

+ 0 - 196
src/components/CloudConnectionDetail/CloudConnectionDetail.js

@@ -1,196 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './CloudConnectionDetail.scss';
-import Moment from 'react-moment';
-import LoadingIcon from '../LoadingIcon';
-import Helper from '../Helper';
-
-const title = 'connection details';
-
-class CloudConnectionDetail extends Component {
-  static propTypes = {
-    connection: PropTypes.object
-  }
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      fields: this.processProps(props),
-      showPassword: false
-    }
-  }
-
-  componentWillMount() {
-    this.context.onSetTitle(title);
-  }
-
-  componentWillReceiveProps(newProps) {
-    this.setState({ fields: this.processProps(newProps) })
-  }
-
-  processProps(props) {
-    let fields = []
-
-    let addField = (name, value) => {
-      name = Helper.convertCloudFieldLabel(name)
-      if (!fields.find(f => f.fieldName === name)) {
-        fields.push({
-          fieldName: name,
-          fieldValue: value
-        })
-      }
-    }
-
-    if (props.connection.credentials) {
-      for (let fieldName in props.connection.credentials) {
-        let value = props.connection.credentials[fieldName]
-        if (value.value) { // if dropdown
-          value = value.value
-        }
-        if (value === true) value = "Yes"
-        if (value === false) value = "No"
-
-        if (typeof value === 'object') {
-          for (let extraField in value) {
-            addField(extraField, value[extraField])
-          }
-        } else {
-          addField(fieldName, value)
-        }
-      }
-    }
-
-    // Sort username and password to the front
-    let sortExceptions = { Username: 1, Password: 2 };
-    fields.sort((a, b) => {
-      if (sortExceptions[a.fieldName] && sortExceptions[b.fieldName]) {
-        return sortExceptions[a.fieldName] - sortExceptions[b.fieldName];
-      } else if (sortExceptions[a.fieldName]) {
-        return -1;
-      } else if (sortExceptions[b.fieldName]) {
-        return 1;
-      } else {
-        return 0;
-      }
-    })
-
-    return fields
-  }
-
-  handleShowPassword() {
-    this.setState({ showPassword: true })
-  }
-
-  renderAuthFields() {
-    let renderPasswordField = field => {
-      if (field.fieldName !== 'Password' || this.state.showPassword) {
-        return (
-          <div className={s.value}>
-            {field.fieldValue || '-'}
-          </div>
-        )
-      }
-
-      return (
-        <div className={s.value + ' ' + s.passwordValue} onClick={this.handleShowPassword.bind(this)}>
-          •••••••••<span className={s.eyeIcon}></span>
-        </div>
-      )
-    }
-
-    if (this.state.fields.length) {
-      return this.state.fields.map((field, index) => {
-        if (field.fieldName !== 'login_type') {
-          return (
-            <div className={s.formGroup} key={"formGroup_" + index}>
-              <div className={s.title}>
-                {field.fieldName}
-              </div>
-              {renderPasswordField(field)}
-            </div>
-          )
-        } else {
-          return null
-        }
-      })
-    } else {
-      return <LoadingIcon />
-    }
-  }
-
-  render() {
-    let item = this.props.connection
-    let createdAt = Helper.getTimeObject(item.created_at)
-    if (item) {
-      return (
-        <div className={s.root}>
-          <div className={s.container}>
-            <div className={s.formGroup}>
-              <div className={s.title}>
-                Name
-              </div>
-              <div className={s.value}>
-                {item.name}
-              </div>
-            </div>
-            <div className={s.formGroup}>
-              <div className={s.title}>
-                Type
-              </div>
-              <div className={s.value}>
-                {Helper.convertCloudLabel(item.type)}
-              </div>
-            </div>
-            <div className={s.formGroup}>
-              <div className={s.title}>
-                Description
-              </div>
-              <div className={s.value}>
-                {item.description == "" ? "-" : item.description}
-              </div>
-            </div>
-            <div className={s.formGroup}>
-              <div className={s.title}>
-                Created
-              </div>
-              <div className={s.value}>
-                <Moment format="MM/DD/YYYY HH:mm" date={createdAt} />
-              </div>
-            </div>
-            {this.renderAuthFields()}
-          </div>
-        </div>
-      )
-    } else {
-      return (<div className={s.root}>
-        <div className={s.container}>
-          <LoadingIcon />
-        </div>
-      </div>)
-    }
-  }
-
-}
-
-export default withStyles(CloudConnectionDetail, s);

+ 0 - 86
src/components/CloudConnectionDetail/CloudConnectionDetail.scss

@@ -1,86 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import '../variables.scss';
-
-.root {
-  :global(.arrow) {
-    width: 36px;
-    height: 28px;
-    margin-top: 30px;
-    margin-left: 130px;
-    position: absolute;
-  }
-}
-
-.container {
-  margin: 0 auto;
-  display: flex;
-  flex-wrap: wrap;
-  justify-content: center;
-  &:after {
-    clear: both;
-    height: 0;
-    display: block;
-    content: ' ';
-  }
-}
-.formGroup {
-  margin-bottom: 32px;
-  flex-grow: 1;
-  width: 50%;
-  .title {
-    font-weight: $weight-regular;
-    font-size: 10px;
-    color: $gray;
-    text-transform: uppercase;
-    margin-bottom: 2px;
-  }
-  .titleIp {
-    font-weight: $weight-semibold;
-    font-size: 14px;
-    color: $gray;
-  }
-  .value {
-    font-weight: $weight-regular;
-    font-size: 14px;
-    color: $black;
-    &.passwordValue {
-      cursor: pointer;
-      display: inline-block;
-    }
-    a {
-      color: $blue;
-    }
-    &:hover .eyeIcon {
-      opacity: 1;
-    }
-    .eyeIcon {
-      opacity: 0;
-      width: 16px;
-      height: 10px;
-      display: inline-block;
-      margin-left: 8px;
-      transition: opacity $animation-swift-out;
-      background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIxNnB4IiBoZWlnaHQ9IjEwcHgiIHZpZXdCb3g9IjAgMCAxNiAxMCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4gICAgICAgIDx0aXRsZT5Db3B5IENvcHk8L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iQ29yaW9saXMiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPiAgICAgICAgPGcgaWQ9IjMxMC1DbG91ZC1FbmRwb2ludC1PdmVydmlldyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTkyMC4wMDAwMDAsIC00MzUuMDAwMDAwKSIgc3Ryb2tlPSIjMDA0NENBIiBzdHJva2Utd2lkdGg9IjEuMiIgZmlsbC1ydWxlPSJub256ZXJvIj4gICAgICAgICAgICA8ZyBpZD0iaWNvbi1yZXZlYWwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkyMC4wMDAwMDAsIDQzMi4wMDAwMDApIj4gICAgICAgICAgICAgICAgPHBhdGggZD0iTTgsMTIuNCBDMTAuMDAyMDQ3NCwxMi40IDEyLjExMzgwMTEsMTEuMjcyMTczNiAxNC4zMzI3Mzc2LDguOTcwNzMzMTMgQzE0Ljg1NTM5NTEsOC40Mjg2NDIyNiAxNC44NTU2MzQ5LDcuNTcwMjM3OTEgMTQuMzMzMjc4OSw3LjAyNzg1NjA0IEMxMi4xMTc3Mzk2LDQuNzI3MzYxMjkgMTAuMDA1OTMxOCwzLjYgOCwzLjYgQzUuOTk3OTUyNjQsMy42IDMuODg2MTk4OTksNC43Mjc4MjYzMiAxLjY2NzI2MjU1LDcuMDI5MjY2NzMgQzEuMTQ0NjA0OTgsNy41NzEzNTc2NCAxLjE0NDM2NTIxLDguNDI5NzYyMDcgMS42NjY3MjEzNSw4Ljk3MjE0MzkzIEMzLjg4MjI2MTAzLDExLjI3MjYzODcgNS45OTQwNjg3NCwxMi40IDgsMTIuNCBaIiBpZD0iU2hhcGUiPjwvcGF0aD4gICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLDggQzEwLDkuMTA1NDY5IDkuMTA1NDY5LDEwIDgsMTAgQzYuODk0NTMxNSwxMCA2LDkuMTA1NDY5IDYsOCBDNiw2Ljg5NDUzMTUgNi44OTQ1MzE1LDYgOCw2IEM5LjEwNTQ2OSw2IDEwLDYuODk0NTMxNSAxMCw4IFoiIGlkPSJTaGFwZSI+PC9wYXRoPiAgICAgICAgICAgIDwvZz4gICAgICAgIDwvZz4gICAgPC9nPjwvc3ZnPg==);
-    }
-  }
-}
-.cloudImg {
-  width: 96px;
-  margin-top: 16px;
-}

+ 0 - 6
src/components/CloudConnectionDetail/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "CloudConnectionDetail",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./CloudConnectionDetail.js"
-}

+ 0 - 236
src/components/CloudConnectionsView/CloudConnectionsView.js

@@ -1,236 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './CloudConnectionsView.scss';
-import Header from '../Header';
-import ConnectionsActions from '../../actions/ConnectionsActions';
-import Location from '../../core/Location';
-import LoadingIcon from '../LoadingIcon';
-import Modal from '../NewModal';
-import AddCloudConnection from '../AddCloudConnection';
-import ConfirmationDialog from '../ConfirmationDialog'
-import ValidateEndpoint from '../ValidateEndpoint';
-
-
-class CloudConnectionsView extends Component {
-
-  static propTypes = {
-    connection: PropTypes.object,
-    connections: PropTypes.array,
-    children: PropTypes.object,
-    connectionId: PropTypes.string
-  }
-
-  static defaultProps = {
-    connection: null
-  }
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      connection: {
-        name: null,
-        type: null,
-        id: null
-      },
-      confirmationDialog: {
-        visible: false,
-        message: "Are you sure?",
-        onConfirm: null,
-        onCancel: null
-      },
-      showModal: false,
-      showValidationModal: false
-    }
-  }
-
-  componentDidMount() {
-    this.context.onSetTitle(this.title);
-  }
-
-  componentWillReceiveProps(newProps) {
-    if (newProps.connections) {
-      let connection = newProps.connections.filter(item => item.id === this.props.connectionId)[0]
-      if (!connection.credentials) {
-        ConnectionsActions.loadConnectionDetail(connection.id)
-      }
-
-      this.setState({ connection: connection })
-    }
-  }
-
-  onConnectionsActionsChange(option) {
-    switch (option.value) {
-      case "delete":
-        ConnectionsActions.deleteConnection(this.state.connection)
-        Location.push('/cloud-endpoints')
-        break
-      default:
-        break
-    }
-  }
-
-  title = ""
-
-  showEditConnectionModal() {
-    this.setState({ showModal: true })
-  }
-
-  deleteConnection() {
-    this.setState({
-      confirmationDialog: {
-        visible: true,
-        onConfirm: () => {
-          this.setState({ confirmationDialog: { visible: false } })
-          ConnectionsActions.deleteConnection(this.state.connection)
-          Location.push('/cloud-endpoints')
-        },
-        onCancel: () => {
-          this.setState({ confirmationDialog: { visible: false } })
-        }
-      }
-    })
-  }
-
-  validateConnection() {
-    this.setState({ showValidationModal: true })
-  }
-
-  closeValidationModal() {
-    this.setState({ showValidationModal: false })
-  }
-
-  closeModal() {
-    this.setState({ showModal: false })
-    let connection = this.props.connections.filter(item => item.id === this.props.connectionId)[0]
-
-    this.setState({ connection: connection })
-  }
-
-  addItem() {
-    this.validateConnection()
-
-    this.setState({ showModal: false })
-    let connection = this.props.connections.filter(item => item.id === this.props.connectionId)[0]
-
-    this.setState({ connection: connection })
-  }
-
-  updateItem(itemAttrs) {
-    let endpoint = this.state.connection
-    for (let i in itemAttrs) {
-      endpoint[i] = itemAttrs[i]
-    }
-    this.setState({
-      connection: endpoint
-    })
-  }
-
-  goBack() {
-    Location.push("/cloud-endpoints")
-  }
-
-  render() {
-    let item = this.state.connection
-    let title = "Edit Connection"
-
-    if (item) {
-      return (
-        <div className={s.root}>
-          <Header title={title} linkUrl="/cloud-endpoints" />
-          <div className={s.connectionHead + " detailViewHead"}>
-            <div className={s.container}>
-              <div className="backBtn" onClick={(e) => this.goBack(e)}></div>
-              <div className={s.connectionTypeImg + " icon endpoint-white "}></div>
-              <div className={s.connectionInfo}>
-                <h2>{item.name}</h2>
-                <p>{item.description}</p>
-              </div>
-            </div>
-          </div>
-          <div className={s.container}>
-            <div className={s.sidebar}>
-
-            </div>
-            <div className={s.content}>
-              <div className={`horizontal-provider-logo ${(item && item.type) || ''}`}></div>
-              {React.cloneElement(this.props.children, { connection: item })}
-              <div className={s.buttons}>
-                <div className={s.leftSide}>
-                  <button onClick={(e) => this.showEditConnectionModal(e)} className="gray">Edit Endpoint</button>
-                  <br />
-                  <button onClick={(e) => this.validateConnection(e)}>Validate Endpoint</button>
-                </div>
-                <div className={s.rightSide}>
-                  <button onClick={(e) => this.deleteConnection(e)} className="wire red" style={{ float: "right" }}>
-                    Delete
-                  </button>
-                </div>
-              </div>
-            </div>
-          </div>
-          <Modal
-            isOpen={this.state.showModal}
-            contentLabel="Add new cloud connection"
-            onRequestClose={this.closeModal.bind(this)}
-          >
-            <AddCloudConnection
-              closeHandle={(e) => this.closeModal(e)}
-              addHandle={(e) => this.addItem(e)}
-              updateHandle={(e) => this.updateItem(e)}
-              connection={item}
-              type="edit"
-            />
-          </Modal>
-          <Modal
-            isOpen={this.state.showValidationModal}
-            contentLabel="Validate Endpoint"
-            contentStyle={{ width: "400px" }}
-            onRequestClose={this.closeValidationModal.bind(this)}
-          >
-            <ValidateEndpoint
-              closeHandle={(e) => this.closeValidationModal(e)}
-              endpoint={item}
-            />
-          </Modal>
-          <ConfirmationDialog
-            visible={this.state.confirmationDialog.visible}
-            message={this.state.confirmationDialog.message}
-            onConfirm={(e) => this.state.confirmationDialog.onConfirm(e)}
-            onCancel={(e) => this.state.confirmationDialog.onCancel(e)}
-          />
-        </div>
-      );
-    } else {
-      return (
-        <div className={s.root}>
-          <Header title={title} linkUrl="/cloud-endpoints" />
-          <div className={s.container}>
-            <LoadingIcon />
-          </div>
-        </div>)
-    }
-  }
-}
-
-export default withStyles(CloudConnectionsView, s);

+ 0 - 126
src/components/CloudConnectionsView/CloudConnectionsView.scss

@@ -1,126 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import '../variables.scss';
-
-$connectionHeaderBg: #D9DCE3;
-
-.connectionHead {
-  background-color: $connectionHeaderBg;
-  margin-top: 64px;
-  padding: 16px;
-  .connectionTypeImg {
-    margin-right: 64px;
-    float: left;
-  }
-  .connectionInfo {
-    float: left;
-    .connectionStatus {
-      width: 80px;
-      border-radius: 4px;
-      color: #FFF;
-      line-height: 16px;
-      display: inline-block;
-      font-size: 10px;
-      text-align: center;
-      &:global(.PAUSED) {
-        background-color: $gray;
-      }
-    }
-    .connectionType {
-      width: 80px;
-      background-color: #FFF;
-      border-radius: 4px;
-      line-height: 14px;
-      font-size: 10px;
-      text-align: center;
-      display: inline-block;
-      text-transform: uppercase;
-      border: 1px solid $blue;
-      color: $blue;
-      margin-right: 16px;
-      &:global(.replica) {
-        border: 1px solid $color-replica;
-        color: $color-replica;
-      }
-    }
-  }
-  .connectionActions {
-    float: right;
-    margin-top: 16px;
-    button {
-      margin-left: 16px;
-      max-width: 96px;
-    }
-    :global(.Dropdown-root) {
-      width: 160px;
-      float: left;
-      margin-right: 16px;
-    }
-  }
-  &:after {
-    clear: both;
-    content: " ";
-    display: block;
-    height: 0;
-  }
-}
-
-.container {
-  margin: 0 auto;
-  padding: 0;
-  max-width: $narrow-content-width;
-}
-.sidebar {
-  padding-top: 32px;
-  width: 128px;
-  float: left;
-  a {
-    display: block;
-    /* Tasks: */
-    font-weight: $weight-regular;
-    font-size: 16px;
-    color: $gray;
-    text-decoration: none;
-    margin-bottom: 16px;
-    &:global(.active) {
-      color: $blue;
-    }
-  }
-}
-.content {
-  float: left;
-  width: 800px;
-  padding-bottom: 32px;
-
-  :global(.horizontal-provider-logo) {
-    height: 128px;
-  }
-}
-
-.buttons {
-  margin-top: 16px;
-  button {
-    margin-bottom: 16px;
-  }
-  .leftSide {
-    float: left;
-  }
-  .rightSide {
-    float: right;
-  }
-}

+ 0 - 6
src/components/CloudConnectionsView/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "CloudConnectionsView",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./CloudConnectionsView.js"
-}

+ 0 - 159
src/components/CloudItem/CloudItem.js

@@ -1,159 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './CloudItem.scss';
-import Dropdown from '../NewDropdown';
-import AddCloudConnection from '../AddCloudConnection';
-import Modal from '../NewModal';
-
-class CloudItem extends Component {
-  static propTypes = {
-    cloud: PropTypes.object,
-    addCredentialsCallback: PropTypes.func,
-    credentialSelected: PropTypes.object,
-    callback: PropTypes.func,
-    exclude: PropTypes.string,
-    selected: PropTypes.bool
-  }
-
-  constructor(props) {
-    super(props);
-
-    let credentialSelected = null
-    if (props.credentialSelected !== null && props.credentialSelected.id !== props.exclude) {
-      props.cloud.credentials.forEach(credential => {
-        if (credential.name === props.credentialSelected.name) {
-          credentialSelected = {
-            label: credential.name,
-            value: credential.id
-          }
-        }
-      })
-    }
-
-    let credentialsOptions = []
-
-    if (this.props.cloud.credentials) {
-      this.props.cloud.credentials.forEach((credential) => {
-        if (credential.id != this.props.exclude) {
-          credentialsOptions.push({ value: credential.id, label: credential.name })
-        }
-      })
-      credentialsOptions.push({ value: "new", label: "Add New ..." })
-    }
-
-    this.state = {
-      showModal: false,
-      credentialsOptions: credentialsOptions,
-      credentialSelected: credentialSelected
-    }
-  }
-
-  componentWillReceiveProps(nextProps) {
-    this.updateCredentialOptions(nextProps)
-    if (nextProps.selected === false && this.props.selected === true) {
-      this.setState({ credentialSelected: null })
-    }
-  }
-
-  onCredentialsChange(credential) {
-    this.setState({ credentialSelected: credential, credential: credential })
-    if (credential.value != "new") {
-      this.props.callback({
-        name: this.props.cloud.name,
-        cloudRef: this.props.cloud,
-        credential: { name: credential.label, id: credential.value }
-      });
-    } else {
-      this.addNew()
-    }
-    return credential;
-  }
-
-  handleConnectionAdded(connection) {
-    let newCredentials = { cloudName: this.props.cloud.name, connection: connection }
-    this.props.addCredentialsCallback(newCredentials)
-    this.onCredentialsChange({ label: connection.name, value: connection.id })
-  }
-
-  closeModal() {
-    this.setState({ showModal: false })
-  }
-
-  /**
-   * Opens new popup
-   */
-  addNew() {
-    this.setState({ showModal: true })
-  }
-
-  updateCredentialOptions(props) {
-    let credentialsOptions = []
-    if (props.cloud.credentials) {
-      props.cloud.credentials.forEach((credential) => {
-        if (credential.id != this.props.exclude) {
-          credentialsOptions.push({ value: credential.id, label: credential.name })
-        }
-      })
-      credentialsOptions.push({ value: "new", label: "Add New ..." })
-    }
-
-    this.setState({ credentialsOptions: credentialsOptions })
-  }
-
-  render() {
-    let colorType = ""
-    let credential
-
-    if (this.props.cloud.credentials == null || this.props.cloud.credentials.length == 0) {
-      credential = <button className="transparent" onClick={(e) => this.addNew(e)}>Add</button>
-      colorType = "dimmer"
-    } else {
-      credential = (<Dropdown
-        options={this.state.credentialsOptions}
-        onChange={(e) => this.onCredentialsChange(e)}
-        placeholder="Select"
-        value={this.state.credentialSelected}
-      />)
-    }
-
-    return (
-      <div className={s.root + " " + (this.props.selected ? s.selected : "")}>
-        <div className={s.container}>
-          <div className={s.cloudImage + " icon large-cloud " + this.props.cloud.name + " " + colorType}></div>
-          {credential}
-        </div>
-        <Modal
-          isOpen={this.state.showModal}
-          contentLabel="Add new cloud connection"
-          onRequestClose={this.closeModal.bind(this)}
-        >
-          <AddCloudConnection
-            closeHandle={(e) => this.closeModal(e)}
-            cloud={this.props.cloud}
-            onConnectionAdded={this.handleConnectionAdded.bind(this)}
-          />
-        </Modal>
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(CloudItem, s);

+ 0 - 53
src/components/CloudItem/CloudItem.scss

@@ -1,53 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import '../variables.scss';
-
-.root {
-  height: 200px;
-  margin-left: 96px;
-  margin-top: 96px;
-  &.selected {
-    :global(.Dropdown-control) {
-      background-color: $blue;
-      color: #FFF;
-      :global(.Dropdown-arrow) {
-        background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTRweCIgaGVpZ2h0PSI3cHgiIHZpZXdCb3g9IjEgNSAxNCA3IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwb2x5Z29uIGlkPSJDb21iaW5lZC1TaGFwZSIgc3Ryb2tlPSJub25lIiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHBvaW50cz0iOCAxMC4zODIzMzc2IDIuNTAyNjA3MDIgNSAxLjgxNTQzMjg5IDUuNjcyNzkyMjEgOCAxMS43Mjc5MjIxIDE0LjE4NDU2NzEgNS42NzI3OTIyMSAxMy40OTczOTMgNSA4IDEwLjM4MjMzNzYiPjwvcG9seWdvbj48L3N2Zz4=');
-      }
-    }
-  }
-}
-
-.cloudImage {
-  margin-bottom: 32px;
-}
-
-.addBtn {
-
-}
-
-.container {
-  min-height: 200px;
-  margin: 0 auto;
-  display: inline-block;
-  text-align: left;
-  width: 192px;
-}
-
-.hidden {
-  display: none;
-}

+ 0 - 6
src/components/CloudItem/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "CloudItem",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./CloudItem.js"
-}

+ 0 - 73
src/components/ConfirmationDialog/ConfirmationDialog.js

@@ -1,73 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './ConfirmationDialog.scss';
-import Modal from '../NewModal';
-
-class ConfirmationDialog extends Component {
-  static propTypes = {
-    message: PropTypes.string,
-    onConfirm: PropTypes.func,
-    onCancel: PropTypes.func,
-    visible: PropTypes.bool
-  }
-
-  static defaultProps = {
-    message: "Are you sure?",
-    visible: false,
-    place: "right"
-  }
-
-  onCancel() {
-    if (this.props.onCancel) {
-      this.props.onCancel()
-    }
-  }
-
-  onConfirm() {
-    if (this.props.onConfirm) {
-      this.props.onConfirm()
-    }
-  }
-
-  render() {
-    if (this.props.visible) {
-      return (
-        <div className={s.root}>
-          <Modal
-            isOpen={this.props.visible}
-            contentLabel="Confirmation"
-            contentStyle={{ width: '250px', padding: '16px' }}
-            onRequestClose={this.onCancel.bind(this)}
-          >
-            <p className={s.message}>{this.props.message}</p>
-            <div className={s.buttons}>
-              <button className={s.leftBtn + " gray"} onClick={(e) => this.onCancel(e)}>Cancel</button>
-              <button className={s.rightBtn} onClick={(e) => this.onConfirm(e)}>Yes</button>
-            </div>
-          </Modal>
-        </div>
-      );
-    } else {
-      return null
-    }
-  }
-}
-
-export default withStyles(ConfirmationDialog, s);

+ 0 - 6
src/components/ConfirmationDialog/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "ConfirmationDialog",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./ConfirmationDialog.js"
-}

+ 0 - 6
src/components/ContactPage/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "ContactPage",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./ContactPage.js"
-}

+ 0 - 48
src/components/ContentPage/ContentPage.js

@@ -1,48 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './ContentPage.scss';
-
-class ContentPage extends Component {
-
-  static propTypes = {
-    path: PropTypes.string.isRequired,
-    content: PropTypes.string.isRequired,
-    title: PropTypes.string,
-  };
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  render() {
-    this.context.onSetTitle(this.props.title);
-    return (
-      <div className={s.root}>
-        <div className={s.container}>
-          {this.props.path === '/' ? null : <h1>{this.props.title}</h1>}
-          <div dangerouslySetInnerHTML={{ __html: this.props.content || '' }} />
-        </div>
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(ContentPage, s);

+ 0 - 6
src/components/ContentPage/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "ContentPage",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./ContentPage.js"
-}

+ 0 - 146
src/components/DropdownButton/DropdownButton.js

@@ -1,146 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './DropdownButton.scss';
-
-class DropdownButton extends React.Component {
-  static propTypes = {
-    className: PropTypes.string,
-    options: PropTypes.array.isRequired,
-    onChange: PropTypes.func,
-    value: PropTypes.object.isRequired,
-    onButtonClick: PropTypes.func,
-    disabled: PropTypes.bool
-  }
-
-  constructor(props) {
-    super(props)
-
-    this.onPageClick = this.onPageClick.bind(this)
-
-    this.state = {
-      value: props.value,
-      options: props.options,
-      showMenu: false
-    }
-  }
-
-  componentDidMount() {
-    window.addEventListener('mousedown', this.onPageClick, false)
-  }
-
-  componentWillUnmount() {
-    window.removeEventListener('mousedown', this.onPageClick, false)
-  }
-
-  onPageClick() {
-    if (!this.itemMouseDown) {
-      this.closeMenu()
-    }
-  }
-
-  onMenuItemClick(option) {
-    if (this.props.onChange) {
-      this.props.onChange(option)
-    }
-
-    this.closeMenu()
-    this.setState({
-      value: option,
-      firstItemHover: false
-    })
-  }
-
-  onLabelClick() {
-    if (this.props.onButtonClick && !this.isDisabled()) {
-      this.props.onButtonClick()
-    }
-  }
-
-  onMenuItemMouseEnter(index) {
-    if (index === 0) {
-      this.setState({ firstItemHover: true })
-    }
-  }
-
-  onMenuItemMouseLeave(index) {
-    if (index === 0) {
-      this.setState({ firstItemHover: false })
-    }
-  }
-
-  isDisabled() {
-    return typeof this.props.disabled !== 'undefined' ? this.props.disabled : false
-  }
-
-  toggleMenu() {
-    if (!this.isDisabled()) {
-      this.setState({ showMenu: !this.state.showMenu })
-    }
-  }
-
-  closeMenu() {
-    this.setState({ showMenu: false })
-  }
-
-  renderMenu() {
-    if (!this.state.showMenu || this.state.options.length === 0) {
-      return null
-    }
-
-    return (
-      <div className={s.menu + (this.state.firstItemHover ? ' ' + s.firstItemHover : '')}>
-        {this.state.options.map((o, i) => (
-          <div key={o.value}
-            className={s.menuItem + (o.value === this.state.value.value ? ' ' + s.selected : '')}
-            onMouseEnter={() => { this.onMenuItemMouseEnter(i) }}
-            onMouseLeave={() => { this.onMenuItemMouseLeave(i) }}
-            onMouseDown={() => { this.itemMouseDown = true }}
-            onMouseUp={() => { this.itemMouseDown = false }}
-            onClick={() => { this.onMenuItemClick(o) }}
-          >{o.label}</div>
-        ))}
-      </div>
-    )
-  }
-
-  render() {
-    let className = this.props.className || ' '
-
-    if (this.isDisabled()) {
-      className += ' ' + s.disabled
-    }
-
-    return (
-      <div className={s.root + ' ' + className}>
-        <div className={s.label}
-          onClick={this.onLabelClick.bind(this)}
-        >{this.state && this.state.value.label}</div>
-        <div className={s.arrow}
-          onClick={this.toggleMenu.bind(this)}
-          onMouseDown={() => { this.itemMouseDown = true }}
-          onMouseUp={() => { this.itemMouseDown = false }}
-        />
-        {this.renderMenu()}
-      </div>
-    )
-  }
-}
-
-export default withStyles(DropdownButton, s);

+ 0 - 115
src/components/DropdownButton/DropdownButton.scss

@@ -1,115 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import '../variables.scss';
-
-.root {
-  display: flex;
-  position: relative;
-  align-items: center;
-  height: 32px;
-  border-radius: 4px;
-  background-color: $blue;
-  border: none;
-  font-size: 14px;
-  color: #FFF;
-  padding: 0;
-  cursor: pointer;
-  &.disabled {
-    opacity: 0.7;
-    cursor: not-allowed;
-  }
-
-  .label {
-    flex-grow: 1;
-    text-align: center;
-    padding-left: 16px;
-  }
-
-  .arrow {
-    width: 32px;
-    height: 28px;
-    background-repeat: no-repeat;
-    background-position: center;
-    background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIxMnB4IiBoZWlnaHQ9IjZweCIgdmlld0JveD0iMCAwIDEyIDYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+Q2hldnJvbi1HcmV5PC90aXRsZT4gICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+ICAgIDxkZWZzPjwvZGVmcz4gICAgPGcgaWQ9IlN5bWJvbHMiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+ICAgICAgICA8ZyBpZD0iRHJvcGRvd24tRmlsbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE3MC4wMDAwMDAsIC0xMy4wMDAwMDApIiBzdHJva2U9IiNGRkZGRkYiPiAgICAgICAgICAgIDxnIGlkPSJDaGV2cm9uLVdoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjguMDAwMDAwLCA4LjAwMDAwMCkiPiAgICAgICAgICAgICAgICA8cG9seWxpbmUgaWQ9IlJlY3RhbmdsZS1Db3B5IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjAwMDAwMCwgNS41MDAwMDApIHJvdGF0ZSgtMzE1LjAwMDAwMCkgdHJhbnNsYXRlKC04LjAwMDAwMCwgLTUuNTAwMDAwKSAiIHBvaW50cz0iMTEuODg5MDg3MyAxLjYxMDkxMjcgMTEuODg5MDg3MyA5LjM4OTA4NzMgNC4xMTA5MTI3IDkuMzg5MDg3MyI+PC9wb2x5bGluZT4gICAgICAgICAgICA8L2c+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4=);
-  }
-
-  .menu {
-    position: absolute;
-    top: 32px;
-    display: flex;
-    flex-direction: column;
-    width: 100%;
-    color: #333;
-    outline: none;
-    overflow: hidden;
-    background-color: white;
-    border: 1px solid $gray;
-    box-shadow: 0 1px 0 rgba(0,0,0,0.06);
-    box-sizing: border-box;
-    margin-top: 8px;
-    border-radius: $border-radius;
-    overflow: visible;
-
-    &:after {
-      content: " ";
-      display: block;
-      position: absolute;
-      border: 1px solid $gray;
-      width: 10px;
-      height: 10px;
-      right: 9px;
-      top: -6px;
-      transform: rotate(135deg);
-      border-color: transparent transparent $gray $gray;
-      background-color: white;
-    }
-
-    &.firstItemHover:after {
-      background-color: $blue;
-    }
-
-    :first-child {
-      border-top-left-radius: $border-radius;
-      border-top-right-radius: $border-radius;
-    }
-
-    :last-child {
-      border-bottom-left-radius: $border-radius;
-      border-bottom-right-radius: $border-radius;
-    }
-
-    .menuItem {
-      box-sizing: border-box;
-      color: rgba(51,51,51,0.8);
-      cursor: pointer;
-      display: block;
-      padding: 8px 16px;
-      background-color: #FFF;
-      text-align: left;
-
-      &:hover {
-        background-color: $blue;
-        color: #FFF;
-      }
-
-      &.selected {
-        font-weight: $weight-semibold;
-      }
-    }
-  }
-}

+ 0 - 6
src/components/DropdownButton/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "DropdownButton",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./DropdownButton.js"
-}

+ 0 - 174
src/components/EditProfile/EditProfile.js

@@ -1,174 +0,0 @@
-/*
- Copyright (C) 2017  Cloudbase Solutions SRL
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './EditProfile.scss';
-import Dropdown from '../NewDropdown';
-import NotificationActions from '../../actions/NotificationActions';
-import UserActions from '../../actions/UserActions';
-
-const title = 'Edit Profile';
-
-class EditProfile extends Component {
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  }
-  static propTypes = {
-    user: PropTypes.object,
-    type: PropTypes.string,
-    closeHandle: PropTypes.func
-  }
-  static defaultProps = {
-    user: null,
-    type: "edit"
-  }
-
-  constructor(props) {
-    super(props)
-
-    this.state = {
-      firstName: props.user.firstName,
-      lastName: props.user.lastName,
-      email: props.user.email,
-      primaryProject: null,
-      requiredFields: [],
-      formSubmitted: false
-    }
-  }
-
-  componentWillMount() {
-    this.context.onSetTitle(title);
-  }
-
-  componentDidMount() {
-
-  }
-
-  handleChangeFirstName(e) {
-    this.setState({ firstName: e.target.value })
-  }
-
-  handleChangeLastName(e) {
-    this.setState({ lastName: e.target.value })
-  }
-
-  handleChangeEmail(e) {
-    this.setState({ email: e.target.value })
-  }
-
-  handleSave() {
-    UserActions.setUserInfo(this.props.user.id, {
-      extra: {
-        firstName: this.state.firstName,
-        lastName: this.state.lastName
-      },
-      email: this.state.email
-    })
-    let valid = true
-
-    for (let i in this.state.currentCloudData) {
-      if (this.state.requiredFields.indexOf(i) > -1 && !this.state.currentCloudData[i]) {
-        valid = false
-      }
-    }
-    if (this.state.connectionName.trim().length == 0) {
-      valid = false
-    }
-    if (!valid) {
-      NotificationActions.notify("Please fill all required fields", "error")
-      this.setState({ formSubmitted: true })
-    } else {
-      // TODO: Save action here
-    }
-  }
-
-  isValid(field) {
-    if (field.required && this.state.formSubmitted) {
-      return this.state.currentCloudData[field.name].length != 0;
-    } else {
-      return true
-    }
-  }
-
-  handleCancel() {
-    this.props.closeHandle();
-  }
-
-  render() {
-    let projectOptions = []
-    if (this.props.user) {
-      projectOptions = this.props.user.projects.map(project => ({ label: project.name, id: project.id }))
-    }
-
-    return (
-      <div className={s.root}>
-        <div className={s.header}>
-          <h3>{title}</h3>
-        </div>
-        <div className={s.container}>
-          <div className={s.fields}>
-            <div className="form-group">
-              <label>First Name</label>
-              <input
-                type="text"
-                placeholder="First Name"
-                onChange={(e) => this.handleChangeFirstName(e)}
-                value={this.state.firstName}
-              />
-            </div>
-            <div className="form-group">
-              <label>Last Name</label>
-              <input
-                type="text"
-                placeholder="Last Name"
-                onChange={(e) => this.handleChangeLastName(e)}
-                value={this.state.lastName}
-              />
-            </div>
-            <div className="form-group">
-              <label>Email</label>
-              <input
-                type="text"
-                placeholder="Email"
-                onChange={(e) => this.handleChangeEmail(e)}
-                value={this.state.email}
-              />
-            </div>
-            <div className="form-group">
-              <label>Main Project</label>
-              <Dropdown
-                options={projectOptions}
-                placeholder="Switch Project"
-                onChange={(e) => this.switchProject(e)}
-                value={this.state.primaryProject}
-              />
-            </div>
-          </div>
-          <div className={s.buttons}>
-            <button className={s.leftBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
-            <button className={s.rightBtn} onClick={(e) => this.handleSave(e)}>Save</button>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(EditProfile, s);

+ 0 - 164
src/components/EditProfile/EditProfile.scss

@@ -1,164 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import '../variables.scss';
-
-.root {
-  :global(.Dropdown-control) {
-    text-align: left;
-  }
-  .buttons {
-    padding-top: 16px;
-    .leftBtn {
-      float: left;
-    }
-    .rightBtn {
-      float: right;
-    }
-    &:after {
-
-    }
-    .centerBtn {
-      margin: 0 auto;
-      width: 256px;
-      display: block;
-    }
-  }
-}
-.header {
-  h3 {
-    /* Add Cloud Endpoint: */
-    font-weight: $weight-light;
-    font-size: 24px;
-    color: $gray-darker;
-    padding: 10px;
-    background-color: $gray-light;
-    text-align: center;
-    margin: 0;
-    border-radius: 4px 4px 0 0;
-  }
-}
-.container {
-  margin: 0 auto;
-  padding: 48px 32px 32px;
-  position: relative;
-  :global(.form-group) {
-    margin-bottom: 16px;
-    &:global(.switch-radio) {
-      width: 100%;
-    }
-  }
-  input[type="text"], input[type="password"] {
-    width: 100%;
-    box-sizing: border-box;
-  }
-  textarea {
-    width: 100%;
-    height: 96px;
-    box-sizing: border-box;
-  }
-  .fields {
-    :global(.form-group):not(:global(.switch-radio)) {
-      width: 50%;
-      float: left;
-      box-sizing: border-box;
-      label {
-        text-align: left;
-        text-transform: uppercase;
-        font-size: 9px;
-        font-weight: 500;
-        color: $black;
-        display: block;
-        margin-bottom: 6px;
-      }
-      &:nth-child(2n) {
-        padding-left: 16px;
-      }
-      &:nth-child(2n+1) {
-        padding-right: 16px;
-      }
-    }
-    &:after {
-      clear: both;
-      display: block;
-      content: " ";
-      height: 0;
-    }
-    .radioOption {
-      margin-bottom: 16px;
-      label {
-        margin-left: 16px;
-      }
-
-    }
-    &.larger {
-      margin-top: 0px;
-    }
-  }
-  &:after {
-    clear: both;
-    display: block;
-    content: " ";
-    height: 0;
-  }
-  .error {
-    input {
-      border-color: $red;
-    }
-    :global(.Dropdown-control) {
-      border-color: $red;
-    }
-  }
-}
-.cloudList {
-  height: 324px;
-  overflow-y: scroll;
-  padding: 8px;
-  .cloudImage {
-    width: 128px;
-    height: 85.5px;
-    background-size: contain;
-  }
-  .cloudContainer {
-    width: 33%;
-    float: left;
-    height: 110px;
-    &:nth-child(3n) {
-      text-align: right;
-    }
-    &:nth-child(3n+2) {
-      text-align: center;
-    }
-    :global(.icon) {
-      transition: transform $animation-swift-out;
-      &:hover {
-        cursor: pointer;
-        transform: scale(1.05);
-      }
-    }
-  }
-  &:after {
-    clear: both;
-    display: block;
-    content: " ";
-    height: 0;
-  }
-}
-.cloudImage {
-  text-align: center;
-  height: 164px;
-}

+ 0 - 6
src/components/EditProfile/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "EditProfile",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./EditProfile.js"
-}

+ 0 - 108
src/components/EndpointLink/EndpointLink.js

@@ -1,108 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { PropTypes } from 'react';
-import Location from '../../core/Location';
-import Reflux from 'reflux';
-import ConnectionStore from '../../stores/ConnectionsStore';
-
-function isLeftClickEvent(event) {
-  return event.button === 0;
-}
-
-function isModifiedEvent(event) {
-  return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
-}
-
-class EndpointLink extends Reflux.Component {
-
-  static propTypes = {
-    connectionId: PropTypes.string
-  };
-
-  constructor(props) {
-    super(props)
-
-    this.store = ConnectionStore;
-
-    this.state = {
-      connection: null
-    }
-  }
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-  }
-
-  componentDidMount() {
-    this.componentWillReceiveProps(this.props)
-  }
-
-  componentWillReceiveProps(props) {
-    if (this.state && this.state.connections) {
-      this.state.connections.forEach(connection => {
-        if (connection.id == props.connectionId) {
-          this.setState({ connection: connection })
-        }
-      })
-    }
-  }
-
-
-  handleClick = (event) => {
-    let allowTransition = true;
-    let clickResult;
-
-    if (this.props && this.props.onClick) {
-      clickResult = this.props.onClick(event);
-    }
-
-    if (isModifiedEvent(event) || !isLeftClickEvent(event)) {
-      return;
-    }
-
-    if (clickResult === false || event.defaultPrevented === true) {
-      allowTransition = false;
-    }
-
-    event.preventDefault();
-
-    if (allowTransition) {
-      const link = event.currentTarget;
-      if (this.props && this.props.to) {
-        Location.push(this.props.to);
-      } else {
-        Location.push({ pathname: link.pathname, search: link.search });
-      }
-    }
-  };
-
-  render() {
-    const { to, ...props } = this.props; // eslint-disable-line no-use-before-define
-    if (this.state && this.state.connection) {
-      return (<a
-        href={Location.createHref(`/cloud-endpoints/${this.state.connection.id}`)}
-        {...props}
-        onClick={this.handleClick}
-      >{this.state.connection.name}</a>)
-    } else {
-      return <div><span className="taskIcon ERROR" />Endpoint missing</div>
-    }
-  }
-}
-
-export default EndpointLink;

+ 0 - 6
src/components/EndpointLink/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "EndpointLink",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./EndpointLink.js"
-}

+ 0 - 216
src/components/EndpointList/EndpointList.js

@@ -1,216 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { PropTypes } from 'react';
-import Reflux from 'reflux';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Location from '../../core/Location';
-import Moment from 'react-moment';
-import s from './EndpointList.scss';
-import AddCloudConnection from '../AddCloudConnection';
-import Modal from '../NewModal';
-import ConnectionsStore from '../../stores/ConnectionsStore';
-import ConnectionsActions from '../../actions/ConnectionsActions';
-import TextTruncate from 'react-text-truncate';
-import UserIcon from '../UserIcon';
-import EndpointUsage from '../EndpointUsage';
-import NotificationIcon from '../NotificationIcon';
-import ProjectsDropdown from '../ProjectsDropdown';
-import MainList from '../MainList';
-import Helper from '../Helper';
-
-
-const title = 'Cloud Endpoints';
-const connectionActions = {
-  delete_action: {
-    label: "Delete",
-    action: (item) => {
-      ConnectionsActions.deleteConnection(item)
-    },
-    confirm: true
-  }
-}
-
-const filters = [
-  {
-    field: "type",
-    options: [
-      { value: null, label: "All" },
-      { value: "opc", label: "Oracle Cloud" },
-      { value: "oracle_vm", label: "Oracle VM Server" },
-      { value: "openstack", label: "Openstack" },
-      { value: "vmware_vsphere", label: "VMware" }
-    ]
-  }
-]
-
-class EndpointList extends Reflux.Component {
-
-  constructor(props) {
-    super(props)
-    this.store = ConnectionsStore
-
-    this.state = {
-      showModal: false,
-      showValidationModal: false,
-      connections: null
-    }
-
-    this.renderItem = this.renderItem.bind(this)
-  }
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-
-    this.context.onSetTitle(title);
-    if (this.state.connections == null) {
-      ConnectionsActions.loadConnections()
-    }
-  }
-
-  connectionDetail(e, item) {
-    Location.push('/cloud-endpoints/' + item.id + "/")
-  }
-
-  refresh() {
-    ConnectionsActions.loadConnections()
-  }
-
-  showNewConnectionModal() {
-    this.setState({ showModal: true })
-  }
-
-  closeModal() {
-    this.setState({ showModal: false })
-  }
-
-  validateConnection() {
-    this.setState({ showValidationModal: true })
-  }
-
-  closeValidationModal() {
-    this.setState({ showValidationModal: false })
-  }
-
-  renderItem(item) {
-    let createdAt = Helper.getTimeObject(item.created_at)
-    return (
-      <div className={"item " + (item.selected ? " selected" : "")} key={"vm_" + item.id}>
-        <span className="cell cell-icon" onClick={(e) => this.connectionDetail(e, item)}>
-          <div className={"icon endpoint"}></div>
-          <span className="details">
-            <TextTruncate line={1} truncateText="..." text={item.name} />
-            <span className={s.description}>{item.description == "" ? "N/A" : item.description}</span>
-          </span>
-        </span>
-        <span className="cell">
-          <div className={s.cloudImage + " icon small-cloud " + item.type}></div>
-        </span>
-        <span className={"cell " + s.composite}>
-          <span className={s.label}>Created</span>
-          <span className={s.value}>
-            <Moment fromNow ago date={createdAt} /> ago
-          </span>
-        </span>
-        <span className={"cell " + s.composite}>
-          <span className={s.label}>Usage</span>
-          <span className={s.value}>
-            <EndpointUsage connectionId={item.id} />
-          </span>
-        </span>
-      </div>
-    )
-  }
-
-  render() {
-    if ((this.state.connections && this.state.connections.length) || this.state.connections == null) {
-      return (
-        <div className={s.root}>
-          <div className={s.container}>
-            <div className={s.pageHeader}>
-              <div className={s.top}>
-                <h1>{title}</h1>
-                <div className={s.topActions}>
-                  <ProjectsDropdown />
-                  <button onClick={(e) => this.showNewConnectionModal(e)}>New</button>
-                  <UserIcon />
-                  <NotificationIcon />
-                </div>
-              </div>
-            </div>
-            <MainList
-              items={this.state.connections}
-              actions={connectionActions}
-              itemName="connection"
-              renderItem={this.renderItem}
-              filters={filters}
-              refresh={this.refresh}
-            />
-          </div>
-          <Modal
-            isOpen={this.state.showModal}
-            contentLabel="Add new cloud connection"
-            onRequestClose={this.closeModal.bind(this)}
-          >
-            <AddCloudConnection closeHandle={(e) => this.closeModal(e)} />
-          </Modal>
-        </div>
-      );
-    } else {
-      return (
-        <div className={s.root}>
-          <div className={s.container}>
-            <div className={s.pageHeader}>
-              <div className={s.top}>
-                <h1>{title}</h1>
-                <div className={s.topActions}>
-                  <ProjectsDropdown />
-                  <button onClick={(e) => this.showNewConnectionModal(e)}>New</button>
-                  <UserIcon />
-                  <NotificationIcon />
-                </div>
-              </div>
-
-              <div className="noResultsLarge">
-                <span className="icon"></span>
-                <h3>You don't have any Cloud Endpoints in this project</h3>
-                <p>A Cloud Endpoint is used for the source<br />
-                  or target of a Replica/Migration
-                </p>
-                <button onClick={(e) => this.showNewConnectionModal(e)}>Create an Endpoint</button>
-              </div>
-            </div>
-          </div>
-          <Modal
-            isOpen={this.state.showModal}
-            contentLabel="Add new cloud connection"
-            onRequestClose={this.closeModal.bind(this)}
-          >
-            <AddCloudConnection closeHandle={(e) => this.closeModal(e)} />
-          </Modal>
-        </div>
-      )
-    }
-  }
-
-}
-
-export default withStyles(EndpointList, s);

Plik diff jest za duży
+ 0 - 186
src/components/EndpointList/EndpointList.scss


+ 0 - 6
src/components/EndpointList/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "EndpointList",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./EndpointList.js"
-}

+ 0 - 74
src/components/EndpointUsage/EndpointUsage.js

@@ -1,74 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { PropTypes } from 'react';
-import Reflux from 'reflux';
-import MigrationStore from '../../stores/MigrationStore';
-
-class EndpointUsage extends Reflux.Component {
-
-  static propTypes = {
-    connectionId: PropTypes.string
-  };
-
-  constructor(props) {
-    super(props)
-
-    this.store = MigrationStore;
-
-    this.state = {
-      connectionId: props.connectionId,
-      migrationCount: 0,
-      replicaCount: 0
-    }
-  }
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-    this.componentWillReceiveProps(this.props)
-  }
-
-  componentWillReceiveProps(props) {
-    if (props.connectionId && this.state.migrations) {
-      let migrationCount = 0
-      let replicaCount = 0
-      this.state.migrations.forEach(migration => {
-        if (migration.destination_endpoint_id === this.state.connectionId ||
-          migration.origin_endpoint_id === this.state.connectionId) {
-          migrationCount++
-        }
-      })
-      this.state.replicas.forEach(replica => {
-        if (replica.destination_endpoint_id === this.state.connectionId ||
-          replica.origin_endpoint_id === this.state.connectionId) {
-          replicaCount++
-        }
-      })
-      this.setState({ migrationCount: migrationCount, replicaCount: replicaCount })
-    }
-  }
-
-  render() {
-    if (this.state && this.state.connectionId) {
-      return <div>{this.state.migrationCount} migrations, {this.state.replicaCount} replicas</div>;
-    } else {
-      return null
-    }
-  }
-}
-
-export default EndpointUsage;

+ 0 - 6
src/components/EndpointUsage/package.json

@@ -1,6 +0,0 @@
-{
-  "name": "EndpointUsage",
-  "version": "0.0.0",
-  "private": true,
-  "main": "./EndpointUsage.js"
-}

+ 0 - 46
src/components/ErrorPage/ErrorPage.js

@@ -1,46 +0,0 @@
-/*
-Copyright (C) 2017  Cloudbase Solutions SRL
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import React, { Component, PropTypes } from 'react';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import s from './ErrorPage.scss';
-
-const title = 'Error';
-
-class ErrorPage extends Component {
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-    onPageNotFound: PropTypes.func.isRequired,
-  };
-
-  componentWillMount() {
-    this.context.onSetTitle(title);
-  }
-
-  render() {
-    return (
-      <div>
-        <h1>{title}</h1>
-        <p>Sorry, an critical error occurred on this page.</p>
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(ErrorPage, s);

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików