mirror of
https://repository.entgra.net/community/device-mgt-core.git
synced 2025-10-06 02:01:45 +00:00
Merge branch 'appm-publisher/feature/eslint' into 'master'
Add ESLint to APPM publisher ui Closes product-iots#294 See merge request entgra/carbon-device-mgt!421
This commit is contained in:
commit
751f2b4bb8
@ -79,6 +79,16 @@
|
||||
<arguments>install</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>lint</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run-script lint</arguments>
|
||||
</configuration>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>prod</id>
|
||||
<goals>
|
||||
|
||||
@ -0,0 +1,325 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react",
|
||||
"babel",
|
||||
"jsx",
|
||||
"prettier"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2016,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"createClass": "createReactClass",
|
||||
"pragma": "React",
|
||||
"version": "16.8.6"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"commonjs": true,
|
||||
"browser": true,
|
||||
"jasmine": true,
|
||||
"es6": true
|
||||
},
|
||||
"globals": {
|
||||
"document": true,
|
||||
"console": true,
|
||||
// Only for development purposes
|
||||
"setTimeout": true,
|
||||
"window" : true
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
// Enforce the spacing around the * in generator functions.
|
||||
"generator-star-spacing": [2, "after"],
|
||||
// Disallow using variables outside the blocks they are defined (especially
|
||||
// since only let and const are used, see "no-var").
|
||||
"block-scoped-var": 2,
|
||||
// Require camel case names
|
||||
"camelcase": 2,
|
||||
// Allow trailing commas for easy list extension. Having them does not
|
||||
// impair readability, but also not required either.
|
||||
"comma-dangle": 0,
|
||||
// Warn about cyclomatic complexity in functions.
|
||||
"complexity": 1,
|
||||
// Don't warn for inconsistent naming when capturing this (not so important
|
||||
// with auto-binding fat arrow functions).
|
||||
"consistent-this": 0,
|
||||
// Enforce curly brace conventions for all control statements.
|
||||
"curly": 2,
|
||||
// Don't require a default case in switch statements. Avoid being forced to
|
||||
// add a bogus default when you know all possible cases are handled.
|
||||
"default-case": 0,
|
||||
// Encourage the use of dot notation whenever possible.
|
||||
"dot-notation": 2,
|
||||
// Allow mixed 'LF' and 'CRLF' as linebreaks.
|
||||
"linebreak-style": 0,
|
||||
// Don't enforce the maximum depth that blocks can be nested.
|
||||
"max-depth": 0,
|
||||
// Maximum length of a line.
|
||||
"max-len": [2, 100, 2, { "ignoreStrings": true, "ignoreUrls": true}],
|
||||
// Maximum depth callbacks can be nested.
|
||||
"max-nested-callbacks": [2, 3],
|
||||
// Don't limit the number of parameters that can be used in a function.
|
||||
"max-params": 0,
|
||||
// Don't limit the maximum number of statement allowed in a function.
|
||||
"max-statements": 0,
|
||||
// Require a capital letter for constructors, only check if all new
|
||||
// operators are followed by a capital letter. Don't warn when capitalized
|
||||
// functions are used without the new operator.
|
||||
"new-cap": [2, {"capIsNew": false}],
|
||||
// Disallow use of the Array constructor.
|
||||
"no-array-constructor": 2,
|
||||
// Allow use of bitwise operators.
|
||||
"no-bitwise": 0,
|
||||
// Disallow use of arguments.caller or arguments.callee.
|
||||
"no-caller": 2,
|
||||
// Disallow the catch clause parameter name being the same as a variable in
|
||||
// the outer scope, to avoid confusion.
|
||||
"no-catch-shadow": 2,
|
||||
// Disallow assignment in conditional expressions.
|
||||
"no-cond-assign": 2,
|
||||
// Allow using the console API.
|
||||
"no-console": 0,
|
||||
// Allow using constant expressions in conditions like while (true)
|
||||
"no-constant-condition": 0,
|
||||
// Allow use of the continue statement.
|
||||
"no-continue": 0,
|
||||
// Disallow control characters in regular expressions.
|
||||
"no-control-regex": 2,
|
||||
// Disallow deletion of variables (deleting properties is fine).
|
||||
"no-delete-var": 2,
|
||||
// Disallow duplicate arguments in functions.
|
||||
"no-dupe-args": 2,
|
||||
// Disallow duplicate keys when creating object literals.
|
||||
"no-dupe-keys": 2,
|
||||
// Disallow multiple empty lines
|
||||
"no-multiple-empty-lines": "error",
|
||||
// Disallow a duplicate case label.
|
||||
"no-duplicate-case": 2,
|
||||
// Disallow else after a return in an if. The else around the second return
|
||||
// here is useless:
|
||||
// if (something) { return false; } else { return true; }
|
||||
"no-else-return": 2,
|
||||
// Disallow empty statements. This will report an error for:
|
||||
// try { something(); } catch (e) {}
|
||||
// but will not report it for:
|
||||
// try { something(); } catch (e) { /* Silencing the error because ...*/ }
|
||||
// which is a valid use case.
|
||||
"no-empty": 2,
|
||||
// Disallow the use of empty character classes in regular expressions.
|
||||
"no-empty-character-class": 2,
|
||||
// Disallow use of labels for anything other then loops and switches.
|
||||
"no-labels": 2,
|
||||
// Disallow use of eval(). We have other APIs to evaluate code in content.
|
||||
"no-eval": 2,
|
||||
// Disallow assigning to the exception in a catch block.
|
||||
"no-ex-assign": 2,
|
||||
// Disallow adding to native types
|
||||
"no-extend-native": 2,
|
||||
// Disallow unnecessary function binding.
|
||||
"no-extra-bind": 2,
|
||||
// Disallow double-negation boolean casts in a boolean context.
|
||||
"no-extra-boolean-cast": 2,
|
||||
// Allow unnecessary parentheses, as they may make the code more readable.
|
||||
"no-extra-parens": 0,
|
||||
// Disallow fallthrough of case statements, except if there is a comment.
|
||||
"no-fallthrough": 2,
|
||||
// Allow the use of leading or trailing decimal points in numeric literals.
|
||||
"no-floating-decimal": 0,
|
||||
// Disallow if as the only statement in an else block.
|
||||
"no-lonely-if": 2,
|
||||
// Disallow use of multiline strings (use template strings instead).
|
||||
"no-multi-str": 2,
|
||||
// Disallow reassignments of native objects.
|
||||
"no-native-reassign": 2,
|
||||
// Disallow nested ternary expressions, they make the code hard to read.
|
||||
"no-nested-ternary": 2,
|
||||
// Allow use of new operator with the require function.
|
||||
"no-new-require": 0,
|
||||
// Disallow use of octal literals.
|
||||
"no-octal": 2,
|
||||
// Allow reassignment of function parameters.
|
||||
"no-param-reassign": 0,
|
||||
// Allow string concatenation with __dirname and __filename (not a node env).
|
||||
"no-path-concat": 0,
|
||||
// Allow use of unary operators, ++ and --.
|
||||
"no-plusplus": 0,
|
||||
// Allow using process.env (not a node environment).
|
||||
"no-process-env": 0,
|
||||
// Allow using process.exit (not a node environment).
|
||||
"no-process-exit": 0,
|
||||
// Disallow usage of __proto__ property.
|
||||
"no-proto": 2,
|
||||
// Disallow declaring the same variable more than once (we use let anyway).
|
||||
"no-redeclare": 2,
|
||||
// Disallow multiple spaces in a regular expression literal.
|
||||
"no-regex-spaces": 2,
|
||||
// Allow reserved words being used as object literal keys.
|
||||
"no-reserved-keys": 0,
|
||||
// Don't restrict usage of specified node modules (not a node environment).
|
||||
"no-restricted-modules": 0,
|
||||
// Disallow use of assignment in return statement. It is preferable for a
|
||||
// single line of code to have only one easily predictable effect.
|
||||
"no-return-assign": 2,
|
||||
// Allow use of javascript: urls.
|
||||
"no-script-url": 0,
|
||||
// Disallow comparisons where both sides are exactly the same.
|
||||
"no-self-compare": 2,
|
||||
// Disallow use of comma operator.
|
||||
"no-sequences": 2,
|
||||
// Warn about declaration of variables already declared in the outer scope.
|
||||
// This isn't an error because it sometimes is useful to use the same name
|
||||
// in a small helper function rather than having to come up with another
|
||||
// random name.
|
||||
// Still, making this a warning can help people avoid being confused.
|
||||
"no-shadow": 0,
|
||||
// Require empty line at end of file
|
||||
"eol-last": "error",
|
||||
// Disallow shadowing of names such as arguments.
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-space-before-semi": 0,
|
||||
// Disallow sparse arrays, eg. let arr = [,,2].
|
||||
// Array destructuring is fine though:
|
||||
// for (let [, breakpointPromise] of aPromises)
|
||||
"no-sparse-arrays": 2,
|
||||
// Allow use of synchronous methods (not a node environment).
|
||||
"no-sync": 0,
|
||||
// Allow the use of ternary operators.
|
||||
"no-ternary": 0,
|
||||
// Don't allow spaces after end of line
|
||||
"no-trailing-spaces": "error",
|
||||
// Disallow throwing literals (eg. throw "error" instead of
|
||||
// throw new Error("error")).
|
||||
"no-throw-literal": 2,
|
||||
// Disallow use of undeclared variables unless mentioned in a /*global */
|
||||
// block. Note that globals from head.js are automatically imported in tests
|
||||
// by the import-headjs-globals rule form the mozilla eslint plugin.
|
||||
"no-undef": 2,
|
||||
// Allow use of undefined variable.
|
||||
"no-undefined": 0,
|
||||
// Disallow the use of Boolean literals in conditional expressions.
|
||||
"no-unneeded-ternary": 2,
|
||||
// Disallow unreachable statements after a return, throw, continue, or break
|
||||
// statement.
|
||||
"no-unreachable": 2,
|
||||
// Allow using variables before they are defined.
|
||||
"no-unused-vars": [2, {"vars": "all", "args": "none"}],
|
||||
// Disallow global and local variables that arent used, but allow unused function arguments.
|
||||
"no-use-before-define": 0,
|
||||
// We use var-only-at-top-level instead of no-var as we allow top level
|
||||
// vars.
|
||||
"no-var": 0,
|
||||
// Allow using TODO/FIXME comments.
|
||||
"no-warning-comments": 0,
|
||||
// Disallow use of the with statement.
|
||||
"no-with": 2,
|
||||
// Dont require method and property shorthand syntax for object literals.
|
||||
// We use this in the code a lot, but not consistently, and this seems more
|
||||
// like something to check at code review time.
|
||||
"object-shorthand": 0,
|
||||
// Allow more than one variable declaration per function.
|
||||
"one-var": 0,
|
||||
// Single quotes should be used.
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
// Require use of the second argument for parseInt().
|
||||
"radix": 2,
|
||||
// Dont require to sort variables within the same declaration block.
|
||||
// Anyway, one-var is disabled.
|
||||
"sort-vars": 0,
|
||||
"space-after-function-name": 0,
|
||||
"space-before-function-parentheses": 0,
|
||||
// Disallow space before function opening parenthesis.
|
||||
//"space-before-function-paren": [2, "never"],
|
||||
// Disable the rule that checks if spaces inside {} and [] are there or not.
|
||||
// Our code is split on conventions, and itd be nice to have 2 rules
|
||||
// instead, one for [] and one for {}. So, disabling until we write them.
|
||||
"space-in-brackets": 0,
|
||||
// Deprecated, will be removed in 1.0.
|
||||
"space-unary-word-ops": 0,
|
||||
// Require a space immediately following the // in a line comment.
|
||||
"spaced-comment": [2, "always"],
|
||||
// Require "use strict" to be defined globally in the script.
|
||||
"strict": [2, "global"],
|
||||
// Disallow comparisons with the value NaN.
|
||||
"use-isnan": 2,
|
||||
// Warn about invalid JSDoc comments.
|
||||
// Disabled for now because of https://github.com/eslint/eslint/issues/2270
|
||||
// The rule fails on some jsdoc comments like in:
|
||||
// devtools/client/webconsole/console-output.js
|
||||
"valid-jsdoc": 0,
|
||||
// Ensure that the results of typeof are compared against a valid string.
|
||||
"valid-typeof": 2,
|
||||
// Allow vars to be declared anywhere in the scope.
|
||||
"vars-on-top": 0,
|
||||
// Dont require immediate function invocation to be wrapped in parentheses.
|
||||
"wrap-iife": 0,
|
||||
// Don't require regex literals to be wrapped in parentheses (which
|
||||
// supposedly prevent them from being mistaken for division operators).
|
||||
"wrap-regex": 0,
|
||||
// Require for-in loops to have an if statement.
|
||||
"guard-for-in": 0,
|
||||
// allow/disallow an empty newline after var statement
|
||||
"newline-after-var": 0,
|
||||
// disallow the use of alert, confirm, and prompt
|
||||
"no-alert": 0,
|
||||
// disallow the use of deprecated react changes and lifecycle methods
|
||||
"react/no-deprecated": 0,
|
||||
// disallow comparisons to null without a type-checking operator
|
||||
"no-eq-null": 0,
|
||||
// disallow overwriting functions written as function declarations
|
||||
"no-func-assign": 0,
|
||||
// disallow use of eval()-like methods
|
||||
"no-implied-eval": 0,
|
||||
// disallow function or variable declarations in nested blocks
|
||||
"no-inner-declarations": 0,
|
||||
// disallow invalid regular expression strings in the RegExp constructor
|
||||
"no-invalid-regexp": 0,
|
||||
// disallow irregular whitespace outside of strings and comments
|
||||
"no-irregular-whitespace": 0,
|
||||
// disallow unnecessary nested blocks
|
||||
"no-lone-blocks": 0,
|
||||
// disallow creation of functions within loops
|
||||
"no-loop-func": 0,
|
||||
// disallow use of new operator when not part of the assignment or
|
||||
// comparison
|
||||
"no-new": 0,
|
||||
// disallow use of new operator for Function object
|
||||
"no-new-func": 0,
|
||||
// disallow use of the Object constructor
|
||||
"no-new-object": 0,
|
||||
// disallows creating new instances of String,Number, and Boolean
|
||||
"no-new-wrappers": 0,
|
||||
// disallow the use of object properties of the global object (Math and
|
||||
// JSON) as functions
|
||||
"no-obj-calls": 0,
|
||||
// disallow use of octal escape sequences in string literals, such as
|
||||
// var foo = "Copyright \251";
|
||||
"no-octal-escape": 0,
|
||||
// disallow use of undefined when initializing variables
|
||||
"no-undef-init": 0,
|
||||
// disallow usage of expressions in statement position
|
||||
"no-unused-expressions": 0,
|
||||
// disallow use of void operator
|
||||
"no-void": 0,
|
||||
// disallow wrapping of non-IIFE statements in parens
|
||||
"no-wrap-func": 0,
|
||||
// require assignment operator shorthand where possible or prohibit it
|
||||
// entirely
|
||||
"operator-assignment": 0,
|
||||
// enforce operators to be placed before or after line breaks
|
||||
"operator-linebreak": 0,
|
||||
// disable chacking prop types
|
||||
"react/prop-types": 0
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"parser": "flow"
|
||||
}
|
||||
@ -16,14 +16,13 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
module.exports = function (api) {
|
||||
module.exports = function(api) {
|
||||
api.cache(true);
|
||||
const presets = [ "@babel/preset-env",
|
||||
"@babel/preset-react" ];
|
||||
const plugins = ["@babel/plugin-proposal-class-properties"];
|
||||
const presets = ['@babel/preset-env', '@babel/preset-react'];
|
||||
const plugins = ['@babel/plugin-proposal-class-properties'];
|
||||
|
||||
return {
|
||||
presets,
|
||||
plugins
|
||||
plugins,
|
||||
};
|
||||
};
|
||||
@ -56,6 +56,12 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"chai": "^4.1.2",
|
||||
"css-loader": "^0.28.11",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-prettier": "4.3.0",
|
||||
"eslint-plugin-babel": "5.3.0",
|
||||
"eslint-plugin-jsx": "0.0.2",
|
||||
"eslint-plugin-prettier": "3.1.0",
|
||||
"eslint-plugin-react": "7.14.2",
|
||||
"express": "^4.17.1",
|
||||
"express-pino-logger": "^4.0.0",
|
||||
"file-loader": "^2.0.0",
|
||||
@ -74,6 +80,7 @@
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pino-colada": "^1.4.5",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prettier": "1.18.1",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-intl": "^2.9.0",
|
||||
@ -96,6 +103,7 @@
|
||||
"build_prod": "NODE_ENV=production NODE_OPTIONS=--max_old_space_size=4096 webpack -p --display errors-only --hide-modules",
|
||||
"build_dev": "NODE_ENV=development webpack -d --watch ",
|
||||
"server": "node-env-run server --exec nodemon | pino-colada",
|
||||
"dev2": "run-p server start"
|
||||
"dev2": "run-p server start",
|
||||
"lint": "eslint \"src/**/*.js\""
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,27 +16,26 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import "antd/dist/antd.less";
|
||||
import RouteWithSubRoutes from "./components/RouteWithSubRoutes";
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Redirect, Switch,
|
||||
} from 'react-router-dom';
|
||||
import axios from "axios";
|
||||
import {Layout, Spin, Result, notification} from "antd";
|
||||
import ConfigContext from "./context/ConfigContext";
|
||||
import React from 'react';
|
||||
import 'antd/dist/antd.less';
|
||||
import RouteWithSubRoutes from './components/RouteWithSubRoutes';
|
||||
import { BrowserRouter as Router, Redirect, Switch } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import { Layout, Spin, Result } from 'antd';
|
||||
import ConfigContext from './context/ConfigContext';
|
||||
|
||||
const {Content} = Layout;
|
||||
const { Content } = Layout;
|
||||
const loadingView = (
|
||||
<Layout>
|
||||
<Content style={{
|
||||
<Content
|
||||
style={{
|
||||
padding: '0 0',
|
||||
paddingTop: 300,
|
||||
backgroundColor: '#fff',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
<Spin tip="Loading..."/>
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Spin tip="Loading..." />
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
@ -44,7 +43,7 @@ const loadingView = (
|
||||
const errorView = (
|
||||
<Result
|
||||
style={{
|
||||
paddingTop: 200
|
||||
paddingTop: 200,
|
||||
}}
|
||||
status="500"
|
||||
title="Error occurred while loading the configuration"
|
||||
@ -53,102 +52,116 @@ const errorView = (
|
||||
);
|
||||
|
||||
class App extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
error: false,
|
||||
config: {}
|
||||
}
|
||||
config: {},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateFavicon();
|
||||
axios.get(
|
||||
window.location.origin + "/publisher/public/conf/config.json",
|
||||
).then(res => {
|
||||
axios
|
||||
.get(window.location.origin + '/publisher/public/conf/config.json')
|
||||
.then(res => {
|
||||
const config = res.data;
|
||||
this.checkUserLoggedIn(config);
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: true
|
||||
})
|
||||
error: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getAndroidEnterpriseToken = (config) => {
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-url?approveApps=true" +
|
||||
"&searchEnabled=true&isPrivateAppsEnabled=true&isWebAppEnabled=true&isOrganizeAppPageVisible=true&isManagedConfigEnabled=true" +
|
||||
"&host=" + window.location.origin,
|
||||
).then(res => {
|
||||
getAndroidEnterpriseToken = config => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-url?approveApps=true' +
|
||||
'&searchEnabled=true&isPrivateAppsEnabled=true&isWebAppEnabled=true&isOrganizeAppPageVisible=true&isManagedConfigEnabled=true' +
|
||||
'&host=' +
|
||||
window.location.origin,
|
||||
)
|
||||
.then(res => {
|
||||
config.androidEnterpriseToken = res.data.data.token;
|
||||
this.setState({
|
||||
loading: false,
|
||||
config: config
|
||||
config: config,
|
||||
});
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch(error => {
|
||||
config.androidEnterpriseToken = null;
|
||||
this.setState({
|
||||
loading: false,
|
||||
config: config
|
||||
})
|
||||
config: config,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
checkUserLoggedIn = (config) => {
|
||||
axios.post(
|
||||
window.location.origin + "/publisher-ui-request-handler/user",
|
||||
"platform=publisher"
|
||||
).then(res => {
|
||||
checkUserLoggedIn = config => {
|
||||
axios
|
||||
.post(
|
||||
window.location.origin + '/publisher-ui-request-handler/user',
|
||||
'platform=publisher',
|
||||
)
|
||||
.then(res => {
|
||||
config.user = res.data.data;
|
||||
const pageURL = window.location.pathname;
|
||||
const lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
|
||||
if (lastURLSegment === "login") {
|
||||
window.location.href = window.location.origin + `/publisher/`;
|
||||
if (lastURLSegment === 'login') {
|
||||
window.location.href = window.location.origin + '/publisher/';
|
||||
} else {
|
||||
this.getAndroidEnterpriseToken(config);
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 401) {
|
||||
const redirectUrl = encodeURI(window.location.href);
|
||||
const pageURL = window.location.pathname;
|
||||
const lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
|
||||
if (lastURLSegment !== "login") {
|
||||
window.location.href = window.location.origin + `/publisher/login?redirect=${redirectUrl}`;
|
||||
if (lastURLSegment !== 'login') {
|
||||
window.location.href =
|
||||
window.location.origin +
|
||||
`/publisher/login?redirect=${redirectUrl}`;
|
||||
} else {
|
||||
this.getAndroidEnterpriseToken(config);
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: true
|
||||
})
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateFavicon = () =>{
|
||||
const link = document.querySelector("link[rel*='icon']") || document.createElement('link');
|
||||
updateFavicon = () => {
|
||||
const link =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement('link');
|
||||
link.type = 'image/x-icon';
|
||||
link.rel = 'shortcut icon';
|
||||
link.href = window.location.origin+'/devicemgt/public/uuf.unit.favicon/img/favicon.png';
|
||||
link.href =
|
||||
window.location.origin +
|
||||
'/devicemgt/public/uuf.unit.favicon/img/favicon.png';
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {loading, error} = this.state;
|
||||
const { loading, error } = this.state;
|
||||
|
||||
const applicationView = (
|
||||
<Router>
|
||||
<ConfigContext.Provider value={this.state.config}>
|
||||
<div>
|
||||
<Switch>
|
||||
<Redirect exact from="/publisher" to="/publisher/apps"/>
|
||||
{this.props.routes.map((route) => (
|
||||
<Redirect exact from="/publisher" to="/publisher/apps" />
|
||||
{this.props.routes.map(route => (
|
||||
<RouteWithSubRoutes key={route.path} {...route} />
|
||||
))}
|
||||
</Switch>
|
||||
|
||||
@ -17,21 +17,24 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {Route} from 'react-router-dom';
|
||||
class RouteWithSubRoutes extends React.Component{
|
||||
import { Route } from 'react-router-dom';
|
||||
class RouteWithSubRoutes extends React.Component {
|
||||
props;
|
||||
constructor(props){
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.props = props;
|
||||
}
|
||||
render() {
|
||||
return(
|
||||
<Route path={this.props.path} exact={this.props.exact} render={(props) => (
|
||||
<this.props.component {...props} routes={this.props.routes}/>
|
||||
)}/>
|
||||
return (
|
||||
<Route
|
||||
path={this.props.path}
|
||||
exact={this.props.exact}
|
||||
render={props => (
|
||||
<this.props.component {...props} routes={this.props.routes} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RouteWithSubRoutes;
|
||||
@ -16,60 +16,70 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Row, Typography, Icon, notification} from "antd";
|
||||
import StarRatings from "react-star-ratings";
|
||||
import "./DetailedRating.css";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
import React from 'react';
|
||||
import { Row, Typography, Icon } from 'antd';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
import './DetailedRating.css';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../js/Utils';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
|
||||
class DetailedRating extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
class DetailedRating extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state={
|
||||
detailedRating: null
|
||||
}
|
||||
this.state = {
|
||||
detailedRating: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {type,uuid} = this.props;
|
||||
this.getData(type,uuid);
|
||||
const { type, uuid } = this.props;
|
||||
this.getData(type, uuid);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps.uuid !== this.props.uuid) {
|
||||
const {type,uuid} = this.props;
|
||||
this.getData(type,uuid);
|
||||
const { type, uuid } = this.props;
|
||||
this.getData(type, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
getData = (type, uuid)=>{
|
||||
getData = (type, uuid) => {
|
||||
const config = this.props.context;
|
||||
return axios.get(
|
||||
window.location.origin+ config.serverConfig.invoker.uri +config.serverConfig.invoker.publisher+"/admin/reviews/"+uuid+"/"+type+"-rating",
|
||||
).then(res => {
|
||||
return axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/reviews/' +
|
||||
uuid +
|
||||
'/' +
|
||||
type +
|
||||
'-rating',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let detailedRating = res.data.data;
|
||||
this.setState({
|
||||
detailedRating
|
||||
})
|
||||
detailedRating,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch(function (error) {
|
||||
handleApiError(error, "Error occurred while trying to load rating for the release.", true);
|
||||
})
|
||||
.catch(function(error) {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load rating for the release.',
|
||||
true,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const detailedRating = this.state.detailedRating;
|
||||
|
||||
|
||||
if(detailedRating ==null){
|
||||
if (detailedRating == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -78,21 +88,22 @@ class DetailedRating extends React.Component{
|
||||
|
||||
const ratingArray = [];
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (let [key, value] of Object.entries(ratingVariety)) {
|
||||
ratingArray.push(value);
|
||||
}
|
||||
|
||||
const maximumRating = Math.max(...ratingArray);
|
||||
|
||||
const ratingBarPercentages = [0,0,0,0,0];
|
||||
const ratingBarPercentages = [0, 0, 0, 0, 0];
|
||||
|
||||
if(maximumRating>0){
|
||||
for(let i = 0; i<5; i++){
|
||||
ratingBarPercentages[i] = (ratingVariety[(i+1).toString()])/maximumRating*100;
|
||||
if (maximumRating > 0) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
ratingBarPercentages[i] =
|
||||
(ratingVariety[(i + 1).toString()] / maximumRating) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Row className="d-rating">
|
||||
<div className="numeric-data">
|
||||
@ -100,34 +111,61 @@ class DetailedRating extends React.Component{
|
||||
<StarRatings
|
||||
rating={detailedRating.ratingValue}
|
||||
starRatedColor="#777"
|
||||
starDimension = "16px"
|
||||
starSpacing = "2px"
|
||||
starDimension="16px"
|
||||
starSpacing="2px"
|
||||
numberOfStars={5}
|
||||
name='rating'
|
||||
name="rating"
|
||||
/>
|
||||
<br/>
|
||||
<Text type="secondary" className="people-count"><Icon type="team" /> {totalCount} total</Text>
|
||||
<br />
|
||||
<Text type="secondary" className="people-count">
|
||||
<Icon type="team" /> {totalCount} total
|
||||
</Text>
|
||||
</div>
|
||||
<div className="bar-containers">
|
||||
<div className="bar-container">
|
||||
<span className="number">5</span>
|
||||
<span className="bar rate-5" style={{width: ratingBarPercentages[4]+"%"}}> </span>
|
||||
<span
|
||||
className="bar rate-5"
|
||||
style={{ width: ratingBarPercentages[4] + '%' }}
|
||||
>
|
||||
{' '}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bar-container">
|
||||
<span className="number">4</span>
|
||||
<span className="bar rate-4" style={{width: ratingBarPercentages[3]+"%"}}> </span>
|
||||
<span
|
||||
className="bar rate-4"
|
||||
style={{ width: ratingBarPercentages[3] + '%' }}
|
||||
>
|
||||
{' '}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bar-container">
|
||||
<span className="number">3</span>
|
||||
<span className="bar rate-3" style={{width: ratingBarPercentages[2]+"%"}}> </span>
|
||||
<span
|
||||
className="bar rate-3"
|
||||
style={{ width: ratingBarPercentages[2] + '%' }}
|
||||
>
|
||||
{' '}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bar-container">
|
||||
<span className="number">2</span>
|
||||
<span className="bar rate-2" style={{width: ratingBarPercentages[1]+"%"}}> </span>
|
||||
<span
|
||||
className="bar rate-2"
|
||||
style={{ width: ratingBarPercentages[1] + '%' }}
|
||||
>
|
||||
{' '}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bar-container">
|
||||
<span className="number">1</span>
|
||||
<span className="bar rate-1" style={{width: ratingBarPercentages[0]+"%"}}> </span>
|
||||
<span
|
||||
className="bar rate-1"
|
||||
style={{ width: ratingBarPercentages[0] + '%' }}
|
||||
>
|
||||
{' '}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
@ -135,5 +173,4 @@ class DetailedRating extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default withConfigContext(DetailedRating);
|
||||
@ -30,27 +30,28 @@ import {
|
||||
Spin,
|
||||
message,
|
||||
Icon,
|
||||
Card, Badge, Tooltip, Popover
|
||||
Card,
|
||||
Badge,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import DetailedRating from "../../detailed-rating/DetailedRating";
|
||||
import {Link} from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import ReactQuill from "react-quill";
|
||||
import DetailedRating from '../../detailed-rating/DetailedRating';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import ReactQuill from 'react-quill';
|
||||
import ReactHtmlParser from 'react-html-parser';
|
||||
import "./AppDetailsDrawer.css";
|
||||
import pSBC from "shade-blend-color";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import ManagedConfigurationsIframe
|
||||
from "../../../manage/android-enterprise/ManagedConfigurationsIframe/ManagedConfigurationsIframe";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
import './AppDetailsDrawer.css';
|
||||
import pSBC from 'shade-blend-color';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import ManagedConfigurationsIframe from '../../../manage/android-enterprise/ManagedConfigurationsIframe/ManagedConfigurationsIframe';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
const {Meta} = Card;
|
||||
const {Text, Title} = Typography;
|
||||
const {Option} = Select;
|
||||
const { Meta } = Card;
|
||||
const { Text, Title } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
const IconText = ({type, text}) => (
|
||||
const IconText = ({ type, text }) => (
|
||||
<span>
|
||||
<Icon type={type} style={{marginRight: 8}}/>
|
||||
<Icon type={type} style={{ marginRight: 8 }} />
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
@ -58,15 +59,20 @@ const IconText = ({type, text}) => (
|
||||
const modules = {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike', 'blockquote'],
|
||||
[{'list': 'ordered'}, {'list': 'bullet'}],
|
||||
['link']
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
['link'],
|
||||
],
|
||||
};
|
||||
|
||||
const formats = [
|
||||
'bold', 'italic', 'underline', 'strike', 'blockquote',
|
||||
'list', 'bullet',
|
||||
'link'
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strike',
|
||||
'blockquote',
|
||||
'list',
|
||||
'bullet',
|
||||
'link',
|
||||
];
|
||||
|
||||
class AppDetailsDrawer extends React.Component {
|
||||
@ -76,7 +82,7 @@ class AppDetailsDrawer extends React.Component {
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
name: "",
|
||||
name: '',
|
||||
description: null,
|
||||
globalCategories: [],
|
||||
globalTags: [],
|
||||
@ -89,7 +95,7 @@ class AppDetailsDrawer extends React.Component {
|
||||
isCategoriesEditEnabled: false,
|
||||
isTagsEditEnabled: false,
|
||||
drawer: null,
|
||||
drawerWidth
|
||||
drawerWidth,
|
||||
};
|
||||
}
|
||||
|
||||
@ -99,7 +105,7 @@ class AppDetailsDrawer extends React.Component {
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (prevProps.app !== this.props.app) {
|
||||
const {name, description, tags, categories} = this.props.app;
|
||||
const { name, description, tags, categories } = this.props.app;
|
||||
this.setState({
|
||||
name,
|
||||
description,
|
||||
@ -114,117 +120,132 @@ class AppDetailsDrawer extends React.Component {
|
||||
|
||||
getCategories = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/categories',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const categories = JSON.parse(res.data.data);
|
||||
this.getTags();
|
||||
const globalCategories = categories.map(category => {
|
||||
return (
|
||||
<Option
|
||||
key={category.categoryName}>
|
||||
<Option key={category.categoryName}>
|
||||
{category.categoryName}
|
||||
</Option>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
globalCategories,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load categories.", true);
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
getTags = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const tags = JSON.parse(res.data.data);
|
||||
|
||||
const globalTags = tags.map(tag => {
|
||||
return (
|
||||
<Option
|
||||
key={tag.tagName}>
|
||||
{tag.tagName}
|
||||
</Option>
|
||||
)
|
||||
return <Option key={tag.tagName}>{tag.tagName}</Option>;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
globalTags,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load tags.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to load tags.');
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// change the app name
|
||||
handleNameSave = name => {
|
||||
const config = this.props.context;
|
||||
const {id} = this.props.app;
|
||||
if (name !== this.state.name && name !== "") {
|
||||
const data = {name: name};
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
|
||||
data
|
||||
).then(res => {
|
||||
const { id } = this.props.app;
|
||||
if (name !== this.state.name && name !== '') {
|
||||
const data = { name: name };
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/' +
|
||||
id,
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const app = res.data.data;
|
||||
this.props.onUpdateApp("name", app.name);
|
||||
notification["success"]({
|
||||
this.props.onUpdateApp('name', app.name);
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'App name updated successfully!'
|
||||
description: 'App name updated successfully!',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
name: app.name,
|
||||
});
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to save the app name.",
|
||||
description: 'Error occurred while trying to save the app name.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// handle description change
|
||||
handleDescriptionChange = (temporaryDescription) => {
|
||||
this.setState({temporaryDescription})
|
||||
handleDescriptionChange = temporaryDescription => {
|
||||
this.setState({ temporaryDescription });
|
||||
};
|
||||
|
||||
enableDescriptionEdit = () => {
|
||||
this.setState({
|
||||
isDescriptionEditEnabled: true,
|
||||
temporaryDescription: this.state.description
|
||||
temporaryDescription: this.state.description,
|
||||
});
|
||||
};
|
||||
|
||||
@ -237,7 +258,7 @@ class AppDetailsDrawer extends React.Component {
|
||||
enableCategoriesEdit = () => {
|
||||
this.setState({
|
||||
isCategoriesEditEnabled: true,
|
||||
temporaryCategories: this.state.categories
|
||||
temporaryCategories: this.state.categories,
|
||||
});
|
||||
};
|
||||
|
||||
@ -248,54 +269,63 @@ class AppDetailsDrawer extends React.Component {
|
||||
};
|
||||
|
||||
// handle description change
|
||||
handleCategoryChange = (temporaryCategories) => {
|
||||
this.setState({temporaryCategories})
|
||||
handleCategoryChange = temporaryCategories => {
|
||||
this.setState({ temporaryCategories });
|
||||
};
|
||||
|
||||
// change app categories
|
||||
handleCategorySave = () => {
|
||||
const config = this.props.context;
|
||||
const {id} = this.props.app;
|
||||
const {temporaryCategories, categories} = this.state;
|
||||
const { id } = this.props.app;
|
||||
const { temporaryCategories, categories } = this.state;
|
||||
|
||||
const difference = temporaryCategories
|
||||
.filter(x => !categories.includes(x))
|
||||
.concat(categories.filter(x => !temporaryCategories.includes(x)));
|
||||
|
||||
if (difference.length !== 0 && temporaryCategories.length !== 0) {
|
||||
const data = {categories: temporaryCategories};
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
|
||||
data
|
||||
).then(res => {
|
||||
const data = { categories: temporaryCategories };
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/' +
|
||||
id,
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const app = res.data.data;
|
||||
this.props.onUpdateApp("categories", temporaryCategories);
|
||||
notification["success"]({
|
||||
this.props.onUpdateApp('categories', temporaryCategories);
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'App categories updated successfully!'
|
||||
description: 'App categories updated successfully!',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
categories: app.categories,
|
||||
isCategoriesEditEnabled: false
|
||||
isCategoriesEditEnabled: false,
|
||||
});
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to updating categories.",
|
||||
'Error occurred while trying to updating categories.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -303,7 +333,7 @@ class AppDetailsDrawer extends React.Component {
|
||||
enableTagsEdit = () => {
|
||||
this.setState({
|
||||
isTagsEditEnabled: true,
|
||||
temporaryTags: this.state.tags
|
||||
temporaryTags: this.state.tags,
|
||||
});
|
||||
};
|
||||
|
||||
@ -314,104 +344,134 @@ class AppDetailsDrawer extends React.Component {
|
||||
};
|
||||
|
||||
// handle description change
|
||||
handleTagsChange = (temporaryTags) => {
|
||||
this.setState({temporaryTags})
|
||||
handleTagsChange = temporaryTags => {
|
||||
this.setState({ temporaryTags });
|
||||
};
|
||||
|
||||
// change app tags
|
||||
handleTagsSave = () => {
|
||||
const config = this.props.context;
|
||||
const {id} = this.props.app;
|
||||
const {temporaryTags, tags} = this.state;
|
||||
|
||||
const { id } = this.props.app;
|
||||
const { temporaryTags, tags } = this.state;
|
||||
|
||||
const difference = temporaryTags
|
||||
.filter(x => !tags.includes(x))
|
||||
.concat(tags.filter(x => !temporaryTags.includes(x)));
|
||||
|
||||
if (difference.length !== 0 && temporaryTags.length !== 0) {
|
||||
const data = {tags: temporaryTags};
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
|
||||
data
|
||||
).then(res => {
|
||||
const data = { tags: temporaryTags };
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/' +
|
||||
id,
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const app = res.data.data;
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'App tags updated successfully!'
|
||||
description: 'App tags updated successfully!',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
tags: app.tags,
|
||||
isTagsEditEnabled: false
|
||||
isTagsEditEnabled: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to update tags",
|
||||
description: 'Error occurred while trying to update tags',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//handle description save
|
||||
// handle description save
|
||||
handleDescriptionSave = () => {
|
||||
const config = this.props.context;
|
||||
const {id} = this.props.app;
|
||||
const {description, temporaryDescription} = this.state;
|
||||
const { id } = this.props.app;
|
||||
const { description, temporaryDescription } = this.state;
|
||||
|
||||
if (temporaryDescription !== description && temporaryDescription !== "<p><br></p>") {
|
||||
const data = {description: temporaryDescription};
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
|
||||
data
|
||||
).then(res => {
|
||||
if (
|
||||
temporaryDescription !== description &&
|
||||
temporaryDescription !== '<p><br></p>'
|
||||
) {
|
||||
const data = { description: temporaryDescription };
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/' +
|
||||
id,
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const app = res.data.data;
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'App description updated successfully!'
|
||||
description: 'App description updated successfully!',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
description: app.description,
|
||||
isDescriptionEditEnabled: false
|
||||
isDescriptionEditEnabled: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
message.error('Something went wrong... :(');
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
} else {
|
||||
this.setState({isDescriptionEditEnabled: false});
|
||||
this.setState({ isDescriptionEditEnabled: false });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
const config = this.props.context;
|
||||
const {app, visible, onClose} = this.props;
|
||||
const { app, visible, onClose } = this.props;
|
||||
const {
|
||||
name, loading, description, isDescriptionEditEnabled, isCategoriesEditEnabled,
|
||||
isTagsEditEnabled, temporaryDescription, temporaryCategories, temporaryTags,
|
||||
globalCategories, globalTags, categories, tags
|
||||
name,
|
||||
loading,
|
||||
description,
|
||||
isDescriptionEditEnabled,
|
||||
isCategoriesEditEnabled,
|
||||
isTagsEditEnabled,
|
||||
temporaryDescription,
|
||||
temporaryCategories,
|
||||
temporaryTags,
|
||||
globalCategories,
|
||||
globalTags,
|
||||
categories,
|
||||
tags,
|
||||
} = this.state;
|
||||
if (app == null) {
|
||||
return null;
|
||||
@ -422,13 +482,15 @@ class AppDetailsDrawer extends React.Component {
|
||||
if (app.applicationReleases.length === 0) {
|
||||
const avatarLetter = name.charAt(0).toUpperCase();
|
||||
avatar = (
|
||||
<Avatar shape="square"
|
||||
<Avatar
|
||||
shape="square"
|
||||
size={100}
|
||||
style={{
|
||||
marginBottom: 10,
|
||||
borderRadius: "28%",
|
||||
backgroundColor: pSBC(0.50, config.theme.primaryColor)
|
||||
}}>
|
||||
borderRadius: '28%',
|
||||
backgroundColor: pSBC(0.5, config.theme.primaryColor),
|
||||
}}
|
||||
>
|
||||
{avatarLetter}
|
||||
</Avatar>
|
||||
);
|
||||
@ -438,12 +500,12 @@ class AppDetailsDrawer extends React.Component {
|
||||
style={{
|
||||
marginBottom: 10,
|
||||
width: 100,
|
||||
borderRadius: "28%",
|
||||
border: "1px solid #ddd"
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
src={app.applicationReleases[0].iconPath}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -453,95 +515,117 @@ class AppDetailsDrawer extends React.Component {
|
||||
width={this.state.drawerWidth}
|
||||
closable={false}
|
||||
onClose={onClose}
|
||||
visible={visible}>
|
||||
visible={visible}
|
||||
>
|
||||
<Spin spinning={loading} delay={500}>
|
||||
<div style={{textAlign: "center"}}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
{avatar}
|
||||
<Title editable={{onChange: this.handleNameSave}} level={2}>{name}</Title>
|
||||
<Title editable={{ onChange: this.handleNameSave }} level={2}>
|
||||
{name}
|
||||
</Title>
|
||||
</div>
|
||||
<Divider/>
|
||||
{/*display manage config button only if the app is public android app*/}
|
||||
{(app.isAndroidEnterpriseApp) &&
|
||||
(config.androidEnterpriseToken !== null) &&
|
||||
(
|
||||
<Divider />
|
||||
{/* display manage config button only if the app is public android app*/}
|
||||
{app.isAndroidEnterpriseApp &&
|
||||
config.androidEnterpriseToken !== null && (
|
||||
<div>
|
||||
<div>
|
||||
<Text strong={true}>Set up managed configurations</Text>
|
||||
</div>
|
||||
<div style={{paddingTop: 16}}>
|
||||
<div style={{ paddingTop: 16 }}>
|
||||
<Text>
|
||||
If you are developing apps for the enterprise market, you may need to satisfy
|
||||
particular requirements set by a organization's policies. Managed
|
||||
configurations,
|
||||
previously known as application restrictions, allow the organization's IT admin
|
||||
to
|
||||
remotely specify settings for apps. This capability is particularly useful for
|
||||
If you are developing apps for the enterprise market, you
|
||||
may need to satisfy particular requirements set by a
|
||||
organization's policies. Managed configurations,
|
||||
previously known as application restrictions, allow the
|
||||
organization's IT admin to remotely specify settings
|
||||
for apps. This capability is particularly useful for
|
||||
organization-approved apps deployed to a work profile.
|
||||
</Text>
|
||||
</div>
|
||||
<br/>
|
||||
<br />
|
||||
<ManagedConfigurationsIframe
|
||||
style={{paddingTop: 16}}
|
||||
packageName={app.packageName}/>
|
||||
<Divider dashed={true}/>
|
||||
style={{ paddingTop: 16 }}
|
||||
packageName={app.packageName}
|
||||
/>
|
||||
<Divider dashed={true} />
|
||||
</div>
|
||||
)}
|
||||
<Text strong={true}>Releases </Text>
|
||||
<div className="releases-details">
|
||||
<List
|
||||
style={{paddingTop: 16}}
|
||||
grid={{gutter: 16, column: 2}}
|
||||
style={{ paddingTop: 16 }}
|
||||
grid={{ gutter: 16, column: 2 }}
|
||||
pagination={{
|
||||
pageSize: 4, // number of releases per page
|
||||
size: "small",
|
||||
size: 'small',
|
||||
}}
|
||||
dataSource={app.applicationReleases}
|
||||
renderItem={release => (
|
||||
<div className="app-release-cards">
|
||||
<List.Item>
|
||||
<Tooltip title="Click to view full details"
|
||||
placement="topRight">
|
||||
<Link to={"apps/releases/" + release.uuid}>
|
||||
<Tooltip
|
||||
title="Click to view full details"
|
||||
placement="topRight"
|
||||
>
|
||||
<Link to={'apps/releases/' + release.uuid}>
|
||||
<Card className="release-card">
|
||||
<Meta
|
||||
avatar={
|
||||
<div>
|
||||
{(release.currentStatus === "PUBLISHED") ? (
|
||||
{release.currentStatus === 'PUBLISHED' ? (
|
||||
<Badge
|
||||
title="Published"
|
||||
count={
|
||||
<Tooltip
|
||||
title="Published">
|
||||
<Tooltip title="Published">
|
||||
<Icon
|
||||
style={{
|
||||
backgroundColor: '#52c41a',
|
||||
borderRadius: "50%",
|
||||
color: "white"
|
||||
borderRadius: '50%',
|
||||
color: 'white',
|
||||
}}
|
||||
type="check-circle"/>
|
||||
type="check-circle"
|
||||
/>
|
||||
</Tooltip>
|
||||
}>
|
||||
<Avatar size="large" shape="square"
|
||||
src={release.iconPath}/>
|
||||
}
|
||||
>
|
||||
<Avatar
|
||||
size="large"
|
||||
shape="square"
|
||||
src={release.iconPath}
|
||||
/>
|
||||
</Badge>
|
||||
) : (
|
||||
<Avatar size="large" shape="square"
|
||||
src={release.iconPath}/>
|
||||
<Avatar
|
||||
size="large"
|
||||
shape="square"
|
||||
src={release.iconPath}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
title={release.version}
|
||||
description={
|
||||
<div style={{
|
||||
fontSize: "0.7em"
|
||||
}} className="description-view">
|
||||
<IconText type="check"
|
||||
text={release.currentStatus}/>
|
||||
<Divider type="vertical"/>
|
||||
<IconText type="upload" text={release.releaseType}/>
|
||||
<Divider type="vertical"/>
|
||||
<IconText type="star-o"
|
||||
text={release.rating.toFixed(1)}/>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '0.7em',
|
||||
}}
|
||||
className="description-view"
|
||||
>
|
||||
<IconText
|
||||
type="check"
|
||||
text={release.currentStatus}
|
||||
/>
|
||||
<Divider type="vertical" />
|
||||
<IconText
|
||||
type="upload"
|
||||
text={release.releaseType}
|
||||
/>
|
||||
<Divider type="vertical" />
|
||||
<IconText
|
||||
type="star-o"
|
||||
text={release.rating.toFixed(1)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@ -554,35 +638,34 @@ class AppDetailsDrawer extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/*display add new release only if app type is enterprise*/}
|
||||
{(app.type === "ENTERPRISE") && (
|
||||
{/* display add new release only if app type is enterprise*/}
|
||||
{app.type === 'ENTERPRISE' && (
|
||||
<div>
|
||||
<Divider dashed={true}/>
|
||||
<div style={{paddingBottom: 16}}>
|
||||
<Text>
|
||||
Add new release for the application
|
||||
</Text>
|
||||
<Divider dashed={true} />
|
||||
<div style={{ paddingBottom: 16 }}>
|
||||
<Text>Add new release for the application</Text>
|
||||
</div>
|
||||
<Link to={`/publisher/apps/${app.deviceType}/${app.id}/add-release`}>
|
||||
<Button
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
size="small">
|
||||
<Link
|
||||
to={`/publisher/apps/${app.deviceType}/${app.id}/add-release`}
|
||||
>
|
||||
<Button htmlType="button" type="primary" size="small">
|
||||
Add
|
||||
</Button>
|
||||
</Link>
|
||||
</div>)}
|
||||
<Divider dashed={true}/>
|
||||
</div>
|
||||
)}
|
||||
<Divider dashed={true} />
|
||||
|
||||
<Text strong={true}>Description </Text>
|
||||
{!isDescriptionEditEnabled && (
|
||||
<Text
|
||||
style={{
|
||||
color: config.theme.primaryColor,
|
||||
cursor: "pointer"
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={this.enableDescriptionEdit}>
|
||||
<Icon type="edit"/>
|
||||
onClick={this.enableDescriptionEdit}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@ -601,111 +684,151 @@ class AppDetailsDrawer extends React.Component {
|
||||
placeholder="Add description"
|
||||
style={{
|
||||
marginBottom: 10,
|
||||
marginTop: 10
|
||||
marginTop: 10,
|
||||
}}
|
||||
/>
|
||||
<Button style={{marginRight: 10}} size="small" htmlType="button"
|
||||
onClick={this.disableDescriptionEdit}>Cancel</Button>
|
||||
<Button size="small" type="primary" htmlType="button"
|
||||
onClick={this.handleDescriptionSave}>Save</Button>
|
||||
<Button
|
||||
style={{ marginRight: 10 }}
|
||||
size="small"
|
||||
htmlType="button"
|
||||
onClick={this.disableDescriptionEdit}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
htmlType="button"
|
||||
onClick={this.handleDescriptionSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Divider dashed={true}/>
|
||||
<Divider dashed={true} />
|
||||
<Text strong={true}>Categories </Text>
|
||||
{!isCategoriesEditEnabled && (<Text
|
||||
style={{color: config.theme.primaryColor, cursor: "pointer"}}
|
||||
onClick={this.enableCategoriesEdit}>
|
||||
<Icon type="edit"/>
|
||||
{!isCategoriesEditEnabled && (
|
||||
<Text
|
||||
style={{ color: config.theme.primaryColor, cursor: 'pointer' }}
|
||||
onClick={this.enableCategoriesEdit}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
</Text>
|
||||
)}
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
{isCategoriesEditEnabled && (
|
||||
<div>
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Please select categories"
|
||||
onChange={this.handleCategoryChange}
|
||||
value={temporaryCategories}
|
||||
>
|
||||
{globalCategories}
|
||||
</Select>
|
||||
<div style={{marginTop: 10}}>
|
||||
<Button style={{marginRight: 10}} size="small" htmlType="button"
|
||||
onClick={this.disableCategoriesEdit}>Cancel</Button>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<Button
|
||||
style={{ marginRight: 10 }}
|
||||
size="small"
|
||||
htmlType="button"
|
||||
onClick={this.disableCategoriesEdit}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
htmlType="button"
|
||||
onClick={this.handleCategorySave}>Save</Button>
|
||||
onClick={this.handleCategorySave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isCategoriesEditEnabled && (
|
||||
<span>{
|
||||
categories.map(category => {
|
||||
<span>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<Tag color={pSBC(0.30, config.theme.primaryColor)} key={category}
|
||||
style={{marginBottom: 5}}>
|
||||
<Tag
|
||||
color={pSBC(0.3, config.theme.primaryColor)}
|
||||
key={category}
|
||||
style={{ marginBottom: 5 }}
|
||||
>
|
||||
{category}
|
||||
</Tag>
|
||||
);
|
||||
})
|
||||
}</span>
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
|
||||
|
||||
<Divider dashed={true}/>
|
||||
<Divider dashed={true} />
|
||||
<Text strong={true}>Tags </Text>
|
||||
{!isTagsEditEnabled && (<Text
|
||||
style={{color: config.theme.primaryColor, cursor: "pointer"}}
|
||||
onClick={this.enableTagsEdit}>
|
||||
<Icon type="edit"/>
|
||||
{!isTagsEditEnabled && (
|
||||
<Text
|
||||
style={{ color: config.theme.primaryColor, cursor: 'pointer' }}
|
||||
onClick={this.enableTagsEdit}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
</Text>
|
||||
)}
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
{isTagsEditEnabled && (
|
||||
<div>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Please select categories"
|
||||
onChange={this.handleTagsChange}
|
||||
value={temporaryTags}
|
||||
>
|
||||
{globalTags}
|
||||
</Select>
|
||||
<div style={{marginTop: 10}}>
|
||||
<Button style={{marginRight: 10}} size="small" htmlType="button"
|
||||
onClick={this.disableTagsEdit}>Cancel</Button>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<Button
|
||||
style={{ marginRight: 10 }}
|
||||
size="small"
|
||||
htmlType="button"
|
||||
onClick={this.disableTagsEdit}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
htmlType="button"
|
||||
onClick={this.handleTagsSave}>Save</Button>
|
||||
onClick={this.handleTagsSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isTagsEditEnabled && (
|
||||
<span>{
|
||||
tags.map(tag => {
|
||||
<span>
|
||||
{tags.map(tag => {
|
||||
return (
|
||||
<Tag color="#34495e" key={tag} style={{marginBottom: 5}}>
|
||||
<Tag color="#34495e" key={tag} style={{ marginBottom: 5 }}>
|
||||
{tag}
|
||||
</Tag>
|
||||
);
|
||||
})
|
||||
}</span>
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<Divider dashed={true}/>
|
||||
<Divider dashed={true} />
|
||||
|
||||
<div className="app-rate">
|
||||
{app.applicationReleases.length > 0 && (
|
||||
<DetailedRating type="app" uuid={app.applicationReleases[0].uuid}/>)}
|
||||
<DetailedRating
|
||||
type="app"
|
||||
uuid={app.applicationReleases[0].uuid}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Spin>
|
||||
</Drawer>
|
||||
|
||||
@ -16,29 +16,24 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
Row,
|
||||
Typography,
|
||||
Input,
|
||||
Divider,
|
||||
Icon,
|
||||
Select,
|
||||
Button,
|
||||
Form,
|
||||
message,
|
||||
Radio,
|
||||
notification, Alert
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
|
||||
const {Option} = Select;
|
||||
const {Title} = Typography;
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../js/Utils';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Title } = Typography;
|
||||
|
||||
class FiltersForm extends React.Component {
|
||||
constructor(props) {
|
||||
@ -50,8 +45,8 @@ class FiltersForm extends React.Component {
|
||||
forbiddenErrors: {
|
||||
categories: false,
|
||||
tags: false,
|
||||
deviceTypes: false
|
||||
}
|
||||
deviceTypes: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -64,12 +59,12 @@ class FiltersForm extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty("deviceType") && values.deviceType === "ALL") {
|
||||
delete values["deviceType"];
|
||||
if (values.hasOwnProperty('deviceType') && values.deviceType === 'ALL') {
|
||||
delete values.deviceType;
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty("appType") && values.appType === "ALL") {
|
||||
delete values["appType"];
|
||||
if (values.hasOwnProperty('appType') && values.appType === 'ALL') {
|
||||
delete values.appType;
|
||||
}
|
||||
|
||||
this.props.setFilters(values);
|
||||
@ -84,29 +79,38 @@ class FiltersForm extends React.Component {
|
||||
|
||||
getCategories = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/categories',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let categories = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
categories: categories,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load categories.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.categories = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -114,9 +118,14 @@ class FiltersForm extends React.Component {
|
||||
|
||||
getTags = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let tags = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
@ -124,30 +133,38 @@ class FiltersForm extends React.Component {
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load tags.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load tags.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.tags = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
getDeviceTypes = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/device-types"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
'/device-types',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const deviceTypes = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
@ -155,175 +172,171 @@ class FiltersForm extends React.Component {
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load device types.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load device types.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.deviceTypes = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {categories, tags, deviceTypes, forbiddenErrors} = this.state;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const { categories, tags, deviceTypes, forbiddenErrors } = this.state;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
|
||||
<Card>
|
||||
<Form labelAlign="left" layout="horizontal"
|
||||
<Form
|
||||
labelAlign="left"
|
||||
layout="horizontal"
|
||||
hideRequiredMark
|
||||
onSubmit={this.handleSubmit}>
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Title level={4}>Filter</Title>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item style={{
|
||||
float: "right",
|
||||
<Form.Item
|
||||
style={{
|
||||
float: 'right',
|
||||
marginBottom: 0,
|
||||
marginTop: -5
|
||||
}}>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
htmlType="submit">
|
||||
marginTop: -5,
|
||||
}}
|
||||
>
|
||||
<Button size="small" type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
{(forbiddenErrors.categories) && (
|
||||
{forbiddenErrors.categories && (
|
||||
<Alert
|
||||
message="You don't have permission to view categories."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item label="Categories">
|
||||
{getFieldDecorator('categories', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: 'Please select categories'
|
||||
}],
|
||||
message: 'Please select categories',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Category"
|
||||
onChange={this.handleCategoryChange}>
|
||||
{
|
||||
categories.map(category => {
|
||||
onChange={this.handleCategoryChange}
|
||||
>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<Option
|
||||
key={category.categoryName}>
|
||||
<Option key={category.categoryName}>
|
||||
{category.categoryName}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
);
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{(forbiddenErrors.deviceTypes) && (
|
||||
{forbiddenErrors.deviceTypes && (
|
||||
<Alert
|
||||
message="You don't have permission to view device types."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item label="Device Type">
|
||||
{getFieldDecorator('deviceType', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: 'Please select device types'
|
||||
}],
|
||||
message: 'Please select device types',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
style={{width: '100%'}}
|
||||
placeholder="Select device types">
|
||||
{
|
||||
deviceTypes.map(deviceType => {
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select device types"
|
||||
>
|
||||
{deviceTypes.map(deviceType => {
|
||||
return (
|
||||
<Option
|
||||
key={deviceType.name}>
|
||||
{deviceType.name}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
<Option
|
||||
key="ALL">All
|
||||
</Option>
|
||||
</Select>
|
||||
<Option key={deviceType.name}>{deviceType.name}</Option>
|
||||
);
|
||||
})}
|
||||
<Option key="ALL">All</Option>
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{(forbiddenErrors.tags) && (
|
||||
{forbiddenErrors.tags && (
|
||||
<Alert
|
||||
message="You don't have permission to view tags."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item label="Tags">
|
||||
{getFieldDecorator('tags', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: 'Please select tags'
|
||||
}],
|
||||
message: 'Please select tags',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select tags"
|
||||
>
|
||||
{
|
||||
tags.map(tag => {
|
||||
return (
|
||||
<Option
|
||||
key={tag.tagName}>
|
||||
{tag.tagName}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
{tags.map(tag => {
|
||||
return <Option key={tag.tagName}>{tag.tagName}</Option>;
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="App Type">
|
||||
{getFieldDecorator('appType', {})(
|
||||
<Select
|
||||
style={{width: '100%'}}
|
||||
placeholder="Select app type"
|
||||
>
|
||||
<Select style={{ width: '100%' }} placeholder="Select app type">
|
||||
<Option value="ENTERPRISE">Enterprise</Option>
|
||||
<Option value="PUBLIC">Public</Option>
|
||||
<Option value="WEB_CLIP">Web APP</Option>
|
||||
<Option value="CUSTOM">Custom</Option>
|
||||
<Option value="ALL">All</Option>
|
||||
</Select>
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Filters = withConfigContext(Form.create({name: 'filter-apps'})(FiltersForm));
|
||||
|
||||
const Filters = withConfigContext(
|
||||
Form.create({ name: 'filter-apps' })(FiltersForm),
|
||||
);
|
||||
|
||||
export default withConfigContext(Filters);
|
||||
@ -16,84 +16,82 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Card, Col, Row, Typography, Input, Divider, notification} from "antd";
|
||||
import AppsTable from "./appsTable/AppsTable";
|
||||
import Filters from "./Filters";
|
||||
import AppDetailsDrawer from "./AppDetailsDrawer/AppDetailsDrawer";
|
||||
import axios from "axios";
|
||||
import React from 'react';
|
||||
import { Card, Col, Row, Typography, Input, Divider } from 'antd';
|
||||
import AppsTable from './appsTable/AppsTable';
|
||||
import Filters from './Filters';
|
||||
|
||||
const {Title} = Typography;
|
||||
const { Title } = Typography;
|
||||
const Search = Input.Search;
|
||||
|
||||
class ListApps extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filters: {}
|
||||
filters: {},
|
||||
};
|
||||
this.appName = '';
|
||||
}
|
||||
|
||||
setFilters = (filters) => {
|
||||
if (this.appName === '' && filters.hasOwnProperty("appName")) {
|
||||
delete filters["appName"];
|
||||
setFilters = filters => {
|
||||
if (this.appName === '' && filters.hasOwnProperty('appName')) {
|
||||
delete filters.appName;
|
||||
} else {
|
||||
filters.appName = this.appName;
|
||||
}
|
||||
this.setState({
|
||||
filters
|
||||
filters,
|
||||
});
|
||||
};
|
||||
|
||||
setSearchText = (appName) => {
|
||||
const filters = {...this.state.filters};
|
||||
setSearchText = appName => {
|
||||
const filters = { ...this.state.filters };
|
||||
this.appName = appName;
|
||||
if (appName === '' && filters.hasOwnProperty("appName")) {
|
||||
delete filters["appName"];
|
||||
if (appName === '' && filters.hasOwnProperty('appName')) {
|
||||
delete filters.appName;
|
||||
} else {
|
||||
filters.appName = appName;
|
||||
}
|
||||
this.setState({
|
||||
filters
|
||||
filters,
|
||||
});
|
||||
};
|
||||
|
||||
onChangeSearchText = (e) => {
|
||||
const filters = {...this.state.filters};
|
||||
onChangeSearchText = e => {
|
||||
const filters = { ...this.state.filters };
|
||||
const appName = e.target.value;
|
||||
if (appName === '' && filters.hasOwnProperty("appName")) {
|
||||
delete filters["appName"];
|
||||
if (appName === '' && filters.hasOwnProperty('appName')) {
|
||||
delete filters.appName;
|
||||
this.setState({
|
||||
filters
|
||||
filters,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {isDrawerVisible, filters} = this.state;
|
||||
const { filters } = this.state;
|
||||
return (
|
||||
<Card>
|
||||
<Row gutter={28}>
|
||||
<Col md={6}>
|
||||
<Filters setFilters={this.setFilters}/>
|
||||
<Filters setFilters={this.setFilters} />
|
||||
</Col>
|
||||
<Col md={18}>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<Title level={4}>Apps</Title>
|
||||
</Col>
|
||||
<Col span={18} style={{textAlign: "right"}}>
|
||||
<Col span={18} style={{ textAlign: 'right' }}>
|
||||
<Search
|
||||
placeholder="Search by app name"
|
||||
onSearch={this.setSearchText}
|
||||
onChange={this.onChangeSearchText}
|
||||
style={{width: 240, zIndex: 0}}
|
||||
style={{ width: 240, zIndex: 0 }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider dashed={true}/>
|
||||
<AppsTable filters={filters}/>
|
||||
<Divider dashed={true} />
|
||||
<AppsTable filters={filters} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Avatar, Table, Tag, Icon, message, notification, Col, Badge, Alert, Tooltip} from "antd";
|
||||
import axios from "axios";
|
||||
import React from 'react';
|
||||
import { Avatar, Table, Tag, Icon, Badge, Alert, Tooltip } from 'antd';
|
||||
import axios from 'axios';
|
||||
import pSBC from 'shade-blend-color';
|
||||
import "./AppsTable.css";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import AppDetailsDrawer from "../AppDetailsDrawer/AppDetailsDrawer";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
import './AppsTable.css';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import AppDetailsDrawer from '../AppDetailsDrawer/AppDetailsDrawer';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
let config = null;
|
||||
|
||||
@ -31,59 +31,72 @@ const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'name',
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: (name, row) => {
|
||||
let avatar = null;
|
||||
if (row.applicationReleases.length === 0) {
|
||||
const avatarLetter = name.charAt(0).toUpperCase();
|
||||
avatar = (
|
||||
<Avatar shape="square" size="large"
|
||||
<Avatar
|
||||
shape="square"
|
||||
size="large"
|
||||
style={{
|
||||
marginRight: 20,
|
||||
borderRadius: "28%",
|
||||
border: "1px solid #ddd",
|
||||
backgroundColor: pSBC(0.50, config.theme.primaryColor)
|
||||
}}>
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
backgroundColor: pSBC(0.5, config.theme.primaryColor),
|
||||
}}
|
||||
>
|
||||
{avatarLetter}
|
||||
</Avatar>
|
||||
);
|
||||
} else {
|
||||
const {applicationReleases} = row;
|
||||
const { applicationReleases } = row;
|
||||
let hasPublishedRelease = false;
|
||||
for (let i = 0; i < applicationReleases.length; i++) {
|
||||
if (applicationReleases[i].currentStatus === "PUBLISHED") {
|
||||
if (applicationReleases[i].currentStatus === 'PUBLISHED') {
|
||||
hasPublishedRelease = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
avatar = (hasPublishedRelease) ? (
|
||||
avatar = hasPublishedRelease ? (
|
||||
<Badge
|
||||
title="Published"
|
||||
style={{backgroundColor: '#52c41a', borderRadius: "50%", color: "white"}}
|
||||
style={{
|
||||
backgroundColor: '#52c41a',
|
||||
borderRadius: '50%',
|
||||
color: 'white',
|
||||
}}
|
||||
count={
|
||||
<Tooltip
|
||||
title="Published">
|
||||
<Tooltip title="Published">
|
||||
<Icon
|
||||
style={{
|
||||
backgroundColor: '#52c41a',
|
||||
borderRadius: "50%",
|
||||
color: "white"
|
||||
borderRadius: '50%',
|
||||
color: 'white',
|
||||
}}
|
||||
type="check-circle"/>
|
||||
type="check-circle"
|
||||
/>
|
||||
</Tooltip>
|
||||
}>
|
||||
<Avatar shape="square" size="large"
|
||||
}
|
||||
>
|
||||
<Avatar
|
||||
shape="square"
|
||||
size="large"
|
||||
style={{
|
||||
borderRadius: "28%",
|
||||
border: "1px solid #ddd"
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
src={row.applicationReleases[0].iconPath}
|
||||
/>
|
||||
</Badge>
|
||||
) : (
|
||||
<Avatar shape="square" size="large"
|
||||
<Avatar
|
||||
shape="square"
|
||||
size="large"
|
||||
style={{
|
||||
borderRadius: "28%",
|
||||
border: "1px solid #ddd"
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
src={row.applicationReleases[0].iconPath}
|
||||
/>
|
||||
@ -93,31 +106,35 @@ const columns = [
|
||||
return (
|
||||
<div>
|
||||
{avatar}
|
||||
<span style={{marginLeft: 20}}>{name}</span>
|
||||
</div>);
|
||||
}
|
||||
<span style={{ marginLeft: 20 }}>{name}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Categories',
|
||||
dataIndex: 'categories',
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: categories => (
|
||||
<span>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<Tag
|
||||
style={{marginBottom: 8}}
|
||||
color={pSBC(0.30, config.theme.primaryColor)}
|
||||
key={category}>
|
||||
style={{ marginBottom: 8 }}
|
||||
color={pSBC(0.3, config.theme.primaryColor)}
|
||||
key={category}
|
||||
>
|
||||
{category}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Platform',
|
||||
dataIndex: 'deviceType',
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: platform => {
|
||||
const defaultPlatformIcons = config.defaultPlatformIcons;
|
||||
let icon = defaultPlatformIcons.default.icon;
|
||||
@ -129,19 +146,19 @@ const columns = [
|
||||
theme = defaultPlatformIcons[platform].theme;
|
||||
}
|
||||
return (
|
||||
<span style={{fontSize: 20, color: color, textAlign: "center"}}>
|
||||
<Icon type={icon} theme={theme}/>
|
||||
<span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
|
||||
<Icon type={icon} theme={theme} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
dataIndex: 'type'
|
||||
dataIndex: 'type',
|
||||
},
|
||||
{
|
||||
title: 'Subscription',
|
||||
dataIndex: 'subMethod'
|
||||
dataIndex: 'subMethod',
|
||||
},
|
||||
];
|
||||
|
||||
@ -156,48 +173,47 @@ class AppsTable extends React.Component {
|
||||
selectedApp: null,
|
||||
selectedAppIndex: -1,
|
||||
loading: false,
|
||||
isForbiddenErrorVisible: false
|
||||
isForbiddenErrorVisible: false,
|
||||
};
|
||||
config = this.props.context;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {filters} = this.props;
|
||||
const { filters } = this.props;
|
||||
this.setState({
|
||||
filters
|
||||
filters,
|
||||
});
|
||||
this.fetch(filters);
|
||||
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
const {filters} = this.props;
|
||||
const { filters } = this.props;
|
||||
if (prevProps.filters !== this.props.filters) {
|
||||
this.setState({
|
||||
filters
|
||||
filters,
|
||||
});
|
||||
this.fetch(filters);
|
||||
}
|
||||
}
|
||||
|
||||
//handler to show app drawer
|
||||
// handler to show app drawer
|
||||
showDrawer = (app, appIndex) => {
|
||||
this.setState({
|
||||
isDrawerVisible: true,
|
||||
selectedApp: app,
|
||||
selectedAppIndex: appIndex
|
||||
selectedAppIndex: appIndex,
|
||||
});
|
||||
};
|
||||
|
||||
// handler to close the app drawer
|
||||
closeDrawer = () => {
|
||||
this.setState({
|
||||
isDrawerVisible: false
|
||||
})
|
||||
isDrawerVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
const pager = {...this.state.pagination};
|
||||
const pager = { ...this.state.pagination };
|
||||
pager.current = pagination.current;
|
||||
|
||||
this.setState({
|
||||
@ -213,31 +229,36 @@ class AppsTable extends React.Component {
|
||||
};
|
||||
|
||||
fetch = (filters, params = {}) => {
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
const config = this.props.context;
|
||||
|
||||
if (!params.hasOwnProperty("page")) {
|
||||
if (!params.hasOwnProperty('page')) {
|
||||
params.page = 1;
|
||||
}
|
||||
|
||||
const data = {
|
||||
offset: 10 * (params.page - 1),
|
||||
limit: 10,
|
||||
...filters
|
||||
...filters,
|
||||
};
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications",
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications',
|
||||
data,
|
||||
).then(res => {
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const data = res.data.data;
|
||||
let apps = [];
|
||||
|
||||
if (res.data.data.hasOwnProperty("applications")) {
|
||||
if (res.data.data.hasOwnProperty('applications')) {
|
||||
apps = data.applications;
|
||||
}
|
||||
const pagination = {...this.state.pagination};
|
||||
const pagination = { ...this.state.pagination };
|
||||
// Read total count from server
|
||||
// pagination.total = data.totalCount;
|
||||
pagination.total = data.pagination.count;
|
||||
@ -247,14 +268,19 @@ class AppsTable extends React.Component {
|
||||
pagination,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load apps.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
this.setState({
|
||||
isForbiddenErrorVisible: true
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load apps.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
this.setState({
|
||||
isForbiddenErrorVisible: true,
|
||||
});
|
||||
}
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
@ -262,20 +288,21 @@ class AppsTable extends React.Component {
|
||||
const apps = [...this.state.apps];
|
||||
apps[this.state.selectedAppIndex][key] = value;
|
||||
this.setState({
|
||||
apps
|
||||
apps,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {isDrawerVisible, loading} = this.state;
|
||||
const { isDrawerVisible, loading } = this.state;
|
||||
return (
|
||||
<div>
|
||||
{(this.state.isForbiddenErrorVisible) && (
|
||||
{this.state.isForbiddenErrorVisible && (
|
||||
<Alert
|
||||
message="You don't have permission to view apps."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<div className="apps-table">
|
||||
<Table
|
||||
@ -292,12 +319,14 @@ class AppsTable extends React.Component {
|
||||
this.showDrawer(record, rowIndex);
|
||||
},
|
||||
};
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
<AppDetailsDrawer
|
||||
visible={isDrawerVisible}
|
||||
onClose={this.closeDrawer}
|
||||
app={this.state.selectedApp}
|
||||
onUpdateApp={this.onUpdateApp}/>
|
||||
onUpdateApp={this.onUpdateApp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,40 +16,39 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Divider, Row, Col, Typography, Button, Drawer, Icon, Tooltip, Empty} from "antd";
|
||||
import StarRatings from "react-star-ratings";
|
||||
import Reviews from "./review/Reviews";
|
||||
import "../../../App.css";
|
||||
import DetailedRating from "../detailed-rating/DetailedRating";
|
||||
import EditRelease from "./edit-release/EditRelease";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import NewAppUploadForm from "../../new-app/subForms/NewAppUploadForm";
|
||||
import React from 'react';
|
||||
import { Divider, Row, Col, Typography, Button, Icon, Tooltip } from 'antd';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
import Reviews from './review/Reviews';
|
||||
import '../../../App.css';
|
||||
import DetailedRating from '../detailed-rating/DetailedRating';
|
||||
import EditRelease from './edit-release/EditRelease';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
|
||||
const {Title, Text, Paragraph} = Typography;
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
class ReleaseView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
}
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
console.log("mounted: Release view");
|
||||
console.log('mounted: Release view');
|
||||
}
|
||||
|
||||
render() {
|
||||
const {app, release} = this.props;
|
||||
const { app, release } = this.props;
|
||||
const config = this.props.context;
|
||||
const {lifecycle, currentLifecycleStatus} = this.props;
|
||||
const { lifecycle, currentLifecycleStatus } = this.props;
|
||||
|
||||
if (release == null || lifecycle == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {isAppUpdatable, isAppInstallable} = lifecycle[currentLifecycleStatus];
|
||||
const { isAppUpdatable, isAppInstallable } = lifecycle[
|
||||
currentLifecycleStatus
|
||||
];
|
||||
|
||||
const platform = app.deviceType;
|
||||
const defaultPlatformIcons = config.defaultPlatformIcons;
|
||||
@ -63,10 +62,10 @@ class ReleaseView extends React.Component {
|
||||
theme = defaultPlatformIcons[platform].theme;
|
||||
}
|
||||
let metaData = [];
|
||||
try{
|
||||
try {
|
||||
metaData = JSON.parse(release.metaData);
|
||||
}catch (e) {
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -74,7 +73,7 @@ class ReleaseView extends React.Component {
|
||||
<div className="release">
|
||||
<Row>
|
||||
<Col xl={4} sm={6} xs={8} className="release-icon">
|
||||
<img src={release.iconPath} alt="icon"/>
|
||||
<img src={release.iconPath} alt="icon" />
|
||||
</Col>
|
||||
<Col xl={10} sm={11} className="release-title">
|
||||
<Title level={2}>{app.name}</Title>
|
||||
@ -84,18 +83,16 @@ class ReleaseView extends React.Component {
|
||||
starDimension="20px"
|
||||
starSpacing="2px"
|
||||
numberOfStars={5}
|
||||
name='rating'
|
||||
name="rating"
|
||||
/>
|
||||
<br/>
|
||||
<br />
|
||||
<Text>Platform : </Text>
|
||||
<span style={{fontSize: 20, color: color, textAlign: "center"}}>
|
||||
<Icon
|
||||
type={icon}
|
||||
theme={theme}
|
||||
/>
|
||||
<span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
|
||||
<Icon type={icon} theme={theme} />
|
||||
</span>
|
||||
<Divider type="vertical"/>
|
||||
<Text>Version : {release.version}</Text><br/>
|
||||
<Divider type="vertical" />
|
||||
<Text>Version : {release.version}</Text>
|
||||
<br />
|
||||
|
||||
<EditRelease
|
||||
forbiddenErrors={this.props.forbiddenErrors}
|
||||
@ -106,64 +103,82 @@ class ReleaseView extends React.Component {
|
||||
updateRelease={this.props.updateRelease}
|
||||
supportedOsVersions={[...this.props.supportedOsVersions]}
|
||||
/>
|
||||
|
||||
</Col>
|
||||
<Col xl={8} md={10} sm={24} xs={24} style={{float: "right"}}>
|
||||
<Col xl={8} md={10} sm={24} xs={24} style={{ float: 'right' }}>
|
||||
<div>
|
||||
<Tooltip
|
||||
title={isAppInstallable ? "Open this app in store" : "This release isn't in an installable state"}>
|
||||
title={
|
||||
isAppInstallable
|
||||
? 'Open this app in store'
|
||||
: "This release isn't in an installable state"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
style={{float: "right"}}
|
||||
style={{ float: 'right' }}
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
icon="shop"
|
||||
disabled={!isAppInstallable}
|
||||
onClick={() => {
|
||||
window.open(window.location.origin + "/store/" + app.deviceType + "/apps/" + release.uuid)
|
||||
}}>
|
||||
window.open(
|
||||
window.location.origin +
|
||||
'/store/' +
|
||||
app.deviceType +
|
||||
'/apps/' +
|
||||
release.uuid,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Open in store
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<Row className="release-images">
|
||||
{release.screenshots.map((screenshotUrl, index) => {
|
||||
return (
|
||||
<div key={index} className="release-screenshot">
|
||||
<img key={screenshotUrl} src={screenshotUrl}/>
|
||||
<img key={screenshotUrl} src={screenshotUrl} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
<Divider/>
|
||||
<Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}>
|
||||
<Divider />
|
||||
<Paragraph type="secondary" ellipsis={{ rows: 3, expandable: true }}>
|
||||
{release.description}
|
||||
</Paragraph>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<Text>META DATA</Text>
|
||||
<Row>
|
||||
{
|
||||
metaData.map((data, index)=>{
|
||||
{metaData.map((data, index) => {
|
||||
return (
|
||||
<Col key={index} lg={8} md={6} xs={24} style={{marginTop:15}}>
|
||||
<Text>{data.key}</Text><br/>
|
||||
<Col
|
||||
key={index}
|
||||
lg={8}
|
||||
md={6}
|
||||
xs={24}
|
||||
style={{ marginTop: 15 }}
|
||||
>
|
||||
<Text>{data.key}</Text>
|
||||
<br />
|
||||
<Text type="secondary">{data.value}</Text>
|
||||
</Col>
|
||||
)
|
||||
})
|
||||
}
|
||||
{(metaData.length===0) && (<Text type="secondary">No meta data available.</Text>)}
|
||||
);
|
||||
})}
|
||||
{metaData.length === 0 && (
|
||||
<Text type="secondary">No meta data available.</Text>
|
||||
)}
|
||||
</Row>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<Text>REVIEWS</Text>
|
||||
<Row>
|
||||
<Col lg={18}>
|
||||
<DetailedRating type="release" uuid={release.uuid}/>
|
||||
<DetailedRating type="release" uuid={release.uuid} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Reviews type="release" uuid={release.uuid}/>
|
||||
<Reviews type="release" uuid={release.uuid} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
@ -26,20 +26,20 @@ import {
|
||||
Tooltip,
|
||||
Upload,
|
||||
Input,
|
||||
Switch,
|
||||
Form,
|
||||
Divider,
|
||||
Row,
|
||||
Col,
|
||||
Select, Alert
|
||||
Select,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import axios from "axios";
|
||||
import "@babel/polyfill";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import axios from 'axios';
|
||||
import '@babel/polyfill';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
|
||||
const {TextArea} = Input;
|
||||
const { TextArea } = Input;
|
||||
const InputGroup = Input.Group;
|
||||
const {Option} = Select;
|
||||
const { Option } = Select;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
@ -74,8 +74,8 @@ class EditReleaseModal extends React.Component {
|
||||
binaryFiles: [],
|
||||
metaData: [],
|
||||
formConfig: {
|
||||
specificElements: {}
|
||||
}
|
||||
specificElements: {},
|
||||
},
|
||||
};
|
||||
this.lowerOsVersion = null;
|
||||
this.upperOsVersion = null;
|
||||
@ -86,126 +86,125 @@ class EditReleaseModal extends React.Component {
|
||||
};
|
||||
|
||||
generateConfig = () => {
|
||||
const {type} = this.props;
|
||||
const { type } = this.props;
|
||||
const formConfig = {
|
||||
type
|
||||
type,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case "ENTERPRISE":
|
||||
formConfig.endpoint = "/ent-app-release";
|
||||
case 'ENTERPRISE':
|
||||
formConfig.endpoint = '/ent-app-release';
|
||||
formConfig.specificElements = {
|
||||
binaryFile: {
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "PUBLIC":
|
||||
formConfig.endpoint = "/public-app-release";
|
||||
case 'PUBLIC':
|
||||
formConfig.endpoint = '/public-app-release';
|
||||
formConfig.specificElements = {
|
||||
packageName: {
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "WEB_CLIP":
|
||||
formConfig.endpoint = "/web-app-release";
|
||||
case 'WEB_CLIP':
|
||||
formConfig.endpoint = '/web-app-release';
|
||||
formConfig.specificElements = {
|
||||
version: {
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
url: {
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "CUSTOM":
|
||||
formConfig.endpoint = "/custom-app-release";
|
||||
case 'CUSTOM':
|
||||
formConfig.endpoint = '/custom-app-release';
|
||||
formConfig.specificElements = {
|
||||
binaryFile: {
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
packageName: {
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
formConfig
|
||||
formConfig,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
showModal = () => {
|
||||
const config = this.props.context;
|
||||
const {app, release} = this.props;
|
||||
const {formConfig} = this.state;
|
||||
const {specificElements} = formConfig;
|
||||
const { release } = this.props;
|
||||
const { formConfig } = this.state;
|
||||
const { specificElements } = formConfig;
|
||||
let metaData = [];
|
||||
|
||||
try {
|
||||
metaData = JSON.parse(release.metaData);
|
||||
} catch (e) {
|
||||
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
this.props.form.setFields({
|
||||
releaseType: {
|
||||
value: release.releaseType
|
||||
value: release.releaseType,
|
||||
},
|
||||
releaseDescription: {
|
||||
value: release.description
|
||||
}
|
||||
value: release.description,
|
||||
},
|
||||
});
|
||||
|
||||
if ((config.deviceTypes.mobileTypes.includes(this.props.deviceType))) {
|
||||
const osVersions = release.supportedOsVersions.split("-");
|
||||
if (config.deviceTypes.mobileTypes.includes(this.props.deviceType)) {
|
||||
const osVersions = release.supportedOsVersions.split('-');
|
||||
this.lowerOsVersion = osVersions[0];
|
||||
this.upperOsVersion = osVersions[1];
|
||||
this.props.form.setFields({
|
||||
lowerOsVersion: {
|
||||
value: osVersions[0]
|
||||
value: osVersions[0],
|
||||
},
|
||||
upperOsVersion: {
|
||||
value: osVersions[1]
|
||||
}
|
||||
value: osVersions[1],
|
||||
},
|
||||
});
|
||||
}
|
||||
if (specificElements.hasOwnProperty("version")) {
|
||||
if (specificElements.hasOwnProperty('version')) {
|
||||
this.props.form.setFields({
|
||||
version: {
|
||||
value: release.version
|
||||
}
|
||||
value: release.version,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty("url")) {
|
||||
if (specificElements.hasOwnProperty('url')) {
|
||||
this.props.form.setFields({
|
||||
url: {
|
||||
value: release.url
|
||||
}
|
||||
value: release.url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty("packageName")) {
|
||||
if (specificElements.hasOwnProperty('packageName')) {
|
||||
this.props.form.setFields({
|
||||
packageName: {
|
||||
value: release.packageName
|
||||
}
|
||||
value: release.packageName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
visible: true,
|
||||
metaData
|
||||
metaData,
|
||||
});
|
||||
};
|
||||
|
||||
@ -228,32 +227,33 @@ class EditReleaseModal extends React.Component {
|
||||
return e && e.fileList;
|
||||
};
|
||||
|
||||
handleIconChange = ({fileList}) => this.setState({icons: fileList});
|
||||
handleBinaryFileChange = ({fileList}) => this.setState({binaryFiles: fileList});
|
||||
|
||||
handleScreenshotChange = ({fileList}) => this.setState({screenshots: fileList});
|
||||
handleIconChange = ({ fileList }) => this.setState({ icons: fileList });
|
||||
handleBinaryFileChange = ({ fileList }) =>
|
||||
this.setState({ binaryFiles: fileList });
|
||||
|
||||
handleScreenshotChange = ({ fileList }) =>
|
||||
this.setState({ screenshots: fileList });
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const {uuid} = this.props.release;
|
||||
const { uuid } = this.props.release;
|
||||
const config = this.props.context;
|
||||
|
||||
const {formConfig} = this.state;
|
||||
const {specificElements} = formConfig;
|
||||
const { formConfig } = this.state;
|
||||
const { specificElements } = formConfig;
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
const {releaseDescription, releaseType} = values;
|
||||
const { releaseDescription, releaseType } = values;
|
||||
|
||||
const {icons, screenshots, binaryFiles} = this.state;
|
||||
const { icons, screenshots, binaryFiles } = this.state;
|
||||
|
||||
const data = new FormData();
|
||||
|
||||
//add release data
|
||||
// add release data
|
||||
const release = {
|
||||
description: releaseDescription,
|
||||
price: 0,
|
||||
@ -262,19 +262,22 @@ class EditReleaseModal extends React.Component {
|
||||
releaseType: releaseType,
|
||||
};
|
||||
|
||||
if ((config.deviceTypes.mobileTypes.includes(this.props.deviceType))) {
|
||||
if (config.deviceTypes.mobileTypes.includes(this.props.deviceType)) {
|
||||
release.supportedOsVersions = `${this.lowerOsVersion}-${this.upperOsVersion}`;
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty("binaryFile") && binaryFiles.length === 1) {
|
||||
if (
|
||||
specificElements.hasOwnProperty('binaryFile') &&
|
||||
binaryFiles.length === 1
|
||||
) {
|
||||
data.append('binaryFile', binaryFiles[0].originFileObj);
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty("version")) {
|
||||
if (specificElements.hasOwnProperty('version')) {
|
||||
release.version = values.version;
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty("url")) {
|
||||
if (specificElements.hasOwnProperty('url')) {
|
||||
release.url = values.url;
|
||||
}
|
||||
|
||||
@ -296,19 +299,24 @@ class EditReleaseModal extends React.Component {
|
||||
|
||||
const json = JSON.stringify(release);
|
||||
const blob = new Blob([json], {
|
||||
type: 'application/json'
|
||||
type: 'application/json',
|
||||
});
|
||||
|
||||
data.append("applicationRelease", blob);
|
||||
data.append('applicationRelease', blob);
|
||||
|
||||
const url = window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications" + formConfig.endpoint + "/" + uuid;
|
||||
const url =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications' +
|
||||
formConfig.endpoint +
|
||||
'/' +
|
||||
uuid;
|
||||
|
||||
axios.put(
|
||||
url,
|
||||
data
|
||||
).then(res => {
|
||||
axios
|
||||
.put(url, data)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
|
||||
const updatedRelease = res.data.data;
|
||||
|
||||
this.setState({
|
||||
@ -316,27 +324,29 @@ class EditReleaseModal extends React.Component {
|
||||
visible: false,
|
||||
});
|
||||
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Saved!",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Saved!',
|
||||
});
|
||||
// console.log(updatedRelease);
|
||||
this.props.updateRelease(updatedRelease);
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
})
|
||||
.catch(error => {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
window.location.href =
|
||||
window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "Something went wrong!",
|
||||
description:
|
||||
"Sorry, we were unable to complete your request.",
|
||||
notification.error({
|
||||
message: 'Something went wrong!',
|
||||
description: 'Sorry, we were unable to complete your request.',
|
||||
});
|
||||
|
||||
}
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -345,11 +355,11 @@ class EditReleaseModal extends React.Component {
|
||||
|
||||
addNewMetaData = () => {
|
||||
this.setState({
|
||||
metaData: this.state.metaData.concat({'key': '', 'value': ''})
|
||||
})
|
||||
metaData: this.state.metaData.concat({ key: '', value: '' }),
|
||||
});
|
||||
};
|
||||
|
||||
handlePreviewCancel = () => this.setState({previewVisible: false});
|
||||
handlePreviewCancel = () => this.setState({ previewVisible: false });
|
||||
|
||||
handlePreview = async file => {
|
||||
if (!file.url && !file.preview) {
|
||||
@ -362,11 +372,11 @@ class EditReleaseModal extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
handleLowerOsVersionChange = (lowerOsVersion) => {
|
||||
handleLowerOsVersionChange = lowerOsVersion => {
|
||||
this.lowerOsVersion = lowerOsVersion;
|
||||
};
|
||||
|
||||
handleUpperOsVersionChange = (upperOsVersion) => {
|
||||
handleUpperOsVersionChange = upperOsVersion => {
|
||||
this.upperOsVersion = upperOsVersion;
|
||||
};
|
||||
|
||||
@ -380,27 +390,33 @@ class EditReleaseModal extends React.Component {
|
||||
metaData,
|
||||
previewImage,
|
||||
previewVisible,
|
||||
binaryFileHelperText,
|
||||
iconHelperText,
|
||||
screenshotHelperText
|
||||
} = this.state;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const {isAppUpdatable, supportedOsVersions, deviceType} = this.props;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const { isAppUpdatable, supportedOsVersions, deviceType } = this.props;
|
||||
const config = this.props.context;
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<Icon type="plus"/>
|
||||
<Icon type="plus" />
|
||||
<div className="ant-upload-text">Select</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tooltip title={isAppUpdatable ? "Edit this release" : "This release isn't in an editable state"}>
|
||||
<Tooltip
|
||||
title={
|
||||
isAppUpdatable
|
||||
? 'Edit this release'
|
||||
: "This release isn't in an editable state"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={!isAppUpdatable}
|
||||
size="small" type="primary" onClick={this.showModal}>
|
||||
<Icon type="edit"/> Edit
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={this.showModal}
|
||||
>
|
||||
<Icon type="edit" /> Edit
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Modal
|
||||
@ -408,19 +424,23 @@ class EditReleaseModal extends React.Component {
|
||||
visible={this.state.visible}
|
||||
footer={null}
|
||||
width={580}
|
||||
onCancel={this.handleCancel}>
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<div>
|
||||
<Spin tip="Uploading..." spinning={loading}>
|
||||
<Form labelAlign="left" layout="horizontal"
|
||||
<Form
|
||||
labelAlign="left"
|
||||
layout="horizontal"
|
||||
hideRequiredMark
|
||||
onSubmit={this.handleSubmit}>
|
||||
{formConfig.specificElements.hasOwnProperty("binaryFile") && (
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{formConfig.specificElements.hasOwnProperty('binaryFile') && (
|
||||
<Form.Item {...formItemLayout} label="Application">
|
||||
{getFieldDecorator('binaryFile', {
|
||||
valuePropName: 'binaryFile',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select application'
|
||||
message: 'Please select application',
|
||||
})(
|
||||
<Upload
|
||||
name="binaryFile"
|
||||
@ -429,7 +449,7 @@ class EditReleaseModal extends React.Component {
|
||||
>
|
||||
{binaryFiles.length !== 1 && (
|
||||
<Button>
|
||||
<Icon type="upload"/> Change
|
||||
<Icon type="upload" /> Change
|
||||
</Button>
|
||||
)}
|
||||
</Upload>,
|
||||
@ -437,29 +457,29 @@ class EditReleaseModal extends React.Component {
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{formConfig.specificElements.hasOwnProperty("url") && (
|
||||
{formConfig.specificElements.hasOwnProperty('url') && (
|
||||
<Form.Item {...formItemLayout} label="URL">
|
||||
{getFieldDecorator('url', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the url'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="url"/>
|
||||
)}
|
||||
message: 'Please input the url',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="url" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{formConfig.specificElements.hasOwnProperty("version") && (
|
||||
{formConfig.specificElements.hasOwnProperty('version') && (
|
||||
<Form.Item {...formItemLayout} label="Version">
|
||||
{getFieldDecorator('version', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the version'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="Version"/>
|
||||
)}
|
||||
message: 'Please input the version',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Version" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
@ -468,14 +488,15 @@ class EditReleaseModal extends React.Component {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon'
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="logo"
|
||||
listType="picture-card"
|
||||
onChange={this.handleIconChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}>
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{icons.length === 1 ? null : uploadButton}
|
||||
</Upload>,
|
||||
)}
|
||||
@ -486,14 +507,15 @@ class EditReleaseModal extends React.Component {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon'
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="screenshots"
|
||||
listType="picture-card"
|
||||
onChange={this.handleScreenshotChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}>
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{screenshots.length >= 3 ? null : uploadButton}
|
||||
</Upload>,
|
||||
)}
|
||||
@ -501,36 +523,44 @@ class EditReleaseModal extends React.Component {
|
||||
|
||||
<Form.Item {...formItemLayout} label="Release Type">
|
||||
{getFieldDecorator('releaseType', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the Release Type'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="Release Type"/>
|
||||
)}
|
||||
message: 'Please input the Release Type',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Release Type" />)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...formItemLayout} label="Description">
|
||||
{getFieldDecorator('releaseDescription', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a description for release'
|
||||
}],
|
||||
message: 'Please enter a description for release',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea placeholder="Enter a description for release"
|
||||
rows={5}/>
|
||||
<TextArea
|
||||
placeholder="Enter a description for release"
|
||||
rows={5}
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{(config.deviceTypes.mobileTypes.includes(deviceType)) && (
|
||||
{config.deviceTypes.mobileTypes.includes(deviceType) && (
|
||||
<div>
|
||||
{(this.props.forbiddenErrors.supportedOsVersions) && (
|
||||
{this.props.forbiddenErrors.supportedOsVersions && (
|
||||
<Alert
|
||||
message="You don't have permission to view supported OS versions."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Supported OS Versions">
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Supported OS Versions"
|
||||
>
|
||||
{getFieldDecorator('supportedOS')(
|
||||
<div>
|
||||
<InputGroup>
|
||||
@ -538,22 +568,27 @@ class EditReleaseModal extends React.Component {
|
||||
<Col span={11}>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('lowerOsVersion', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select Value'
|
||||
}],
|
||||
message: 'Please select Value',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
placeholder="Lower version"
|
||||
style={{width: "100%"}}
|
||||
onChange={this.handleLowerOsVersionChange}>
|
||||
style={{ width: '100%' }}
|
||||
onChange={this.handleLowerOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option key={version.versionName}
|
||||
value={version.versionName}>
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
@ -563,43 +598,49 @@ class EditReleaseModal extends React.Component {
|
||||
<Col span={11}>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('upperOsVersion', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select Value'
|
||||
}],
|
||||
message: 'Please select Value',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select style={{width: "100%"}}
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Upper version"
|
||||
onChange={this.handleUpperOsVersionChange}>
|
||||
onChange={this.handleUpperOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option key={version.versionName}
|
||||
value={version.versionName}>
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Meta Data">
|
||||
{getFieldDecorator('meta', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please fill empty fields'
|
||||
}],
|
||||
initialValue: false
|
||||
message: 'Please fill empty fields',
|
||||
},
|
||||
],
|
||||
initialValue: false,
|
||||
})(
|
||||
<div>
|
||||
{
|
||||
metaData.map((data, index) => {
|
||||
{metaData.map((data, index) => {
|
||||
return (
|
||||
<InputGroup key={index}>
|
||||
<Row gutter={8}>
|
||||
@ -607,65 +648,79 @@ class EditReleaseModal extends React.Component {
|
||||
<Input
|
||||
placeholder="key"
|
||||
value={data.key}
|
||||
onChange={(e) => {
|
||||
metaData[index]['key'] = e.currentTarget.value;
|
||||
onChange={e => {
|
||||
metaData[index].key = e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData
|
||||
})
|
||||
}}/>
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Input
|
||||
placeholder="value"
|
||||
value={data.value}
|
||||
onChange={(e) => {
|
||||
metaData[index].value = e.currentTarget.value;
|
||||
onChange={e => {
|
||||
metaData[index].value =
|
||||
e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData
|
||||
metaData,
|
||||
});
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<Button type="dashed"
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="circle"
|
||||
icon="minus"
|
||||
onClick={() => {
|
||||
metaData.splice(index, 1);
|
||||
this.setState({
|
||||
metaData
|
||||
metaData,
|
||||
});
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
<Button type="dashed" icon="plus" onClick={this.addNewMetaData}>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
type="dashed"
|
||||
icon="plus"
|
||||
onClick={this.addNewMetaData}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</div>,
|
||||
)}
|
||||
|
||||
</Form.Item>
|
||||
<Divider/>
|
||||
<Form.Item style={{float: "right", marginLeft: 8}}>
|
||||
<Divider />
|
||||
<Form.Item style={{ float: 'right', marginLeft: 8 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Update
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item style={{float: "right"}}>
|
||||
<Form.Item style={{ float: 'right' }}>
|
||||
<Button htmlType="button" onClick={this.handleCancel}>
|
||||
Back
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<br/>
|
||||
<br />
|
||||
</Form>
|
||||
</Spin>
|
||||
</div>
|
||||
<Modal visible={previewVisible} footer={null} onCancel={this.handlePreviewCancel}>
|
||||
<img alt="Preview Image" style={{width: '100%'}} src={previewImage}/>
|
||||
<Modal
|
||||
visible={previewVisible}
|
||||
footer={null}
|
||||
onCancel={this.handlePreviewCancel}
|
||||
>
|
||||
<img
|
||||
alt="Preview Image"
|
||||
style={{ width: '100%' }}
|
||||
src={previewImage}
|
||||
/>
|
||||
</Modal>
|
||||
</Modal>
|
||||
</div>
|
||||
@ -673,6 +728,8 @@ class EditReleaseModal extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const EditRelease = withConfigContext(Form.create({name: 'add-new-release'})(EditReleaseModal));
|
||||
const EditRelease = withConfigContext(
|
||||
Form.create({ name: 'add-new-release' })(EditReleaseModal),
|
||||
);
|
||||
|
||||
export default EditRelease;
|
||||
|
||||
@ -16,38 +16,51 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Typography, Tag, Divider, Select, Button, Modal, message, notification, Collapse} from "antd";
|
||||
import axios from "axios";
|
||||
import React from 'react';
|
||||
import {
|
||||
Typography,
|
||||
Tag,
|
||||
Divider,
|
||||
Select,
|
||||
Button,
|
||||
Modal,
|
||||
notification,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import ReactQuill from 'react-quill';
|
||||
import 'react-quill/dist/quill.snow.css';
|
||||
import './LifeCycle.css';
|
||||
import LifeCycleDetailsModal from "./lifeCycleDetailsModal/lifeCycleDetailsModal";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
import LifeCycleDetailsModal from './lifeCycleDetailsModal/lifeCycleDetailsModal';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
const {Text, Title, Paragraph} = Typography;
|
||||
const {Option} = Select;
|
||||
const { Text, Title, Paragraph } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
const modules = {
|
||||
toolbar: [
|
||||
[{'header': [1, 2, false]}],
|
||||
[{ header: [1, 2, false] }],
|
||||
['bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block'],
|
||||
[{'list': 'ordered'}, {'list': 'bullet'}],
|
||||
['link', 'image']
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
['link', 'image'],
|
||||
],
|
||||
};
|
||||
|
||||
const formats = [
|
||||
'header',
|
||||
'bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block',
|
||||
'list', 'bullet',
|
||||
'link', 'image'
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strike',
|
||||
'blockquote',
|
||||
'code-block',
|
||||
'list',
|
||||
'bullet',
|
||||
'link',
|
||||
'image',
|
||||
];
|
||||
|
||||
|
||||
class LifeCycle extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -55,109 +68,127 @@ class LifeCycle extends React.Component {
|
||||
selectedStatus: null,
|
||||
reasonText: '',
|
||||
isReasonModalVisible: false,
|
||||
isConfirmButtonLoading: false
|
||||
}
|
||||
isConfirmButtonLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (prevProps.currentStatus !== this.props.currentStatus || prevProps.uuid !== this.props.uuid) {
|
||||
if (
|
||||
prevProps.currentStatus !== this.props.currentStatus ||
|
||||
prevProps.uuid !== this.props.uuid
|
||||
) {
|
||||
this.setState({
|
||||
currentStatus: this.props.currentStatus
|
||||
currentStatus: this.props.currentStatus,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (value) => {
|
||||
this.setState({reasonText: value})
|
||||
handleChange = value => {
|
||||
this.setState({ reasonText: value });
|
||||
};
|
||||
|
||||
handleSelectChange = (value) => {
|
||||
this.setState({selectedStatus: value})
|
||||
handleSelectChange = value => {
|
||||
this.setState({ selectedStatus: value });
|
||||
};
|
||||
|
||||
showReasonModal = () => {
|
||||
this.setState({
|
||||
isReasonModalVisible: true
|
||||
isReasonModalVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
closeReasonModal = () => {
|
||||
this.setState({
|
||||
isReasonModalVisible: false
|
||||
isReasonModalVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
addLifeCycle = () => {
|
||||
const config = this.props.context;
|
||||
const {selectedStatus, reasonText} = this.state;
|
||||
const {uuid} = this.props;
|
||||
const { selectedStatus, reasonText } = this.state;
|
||||
const { uuid } = this.props;
|
||||
const data = {
|
||||
action: selectedStatus,
|
||||
reason: reasonText
|
||||
reason: reasonText,
|
||||
};
|
||||
|
||||
this.setState({
|
||||
isConfirmButtonLoading: true,
|
||||
});
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/life-cycle/" + uuid,
|
||||
data
|
||||
).then(res => {
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/life-cycle/' +
|
||||
uuid,
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 201) {
|
||||
this.setState({
|
||||
isReasonModalVisible: false,
|
||||
isConfirmButtonLoading: false,
|
||||
currentStatus: selectedStatus,
|
||||
selectedStatus: null,
|
||||
reasonText: ''
|
||||
reasonText: '',
|
||||
});
|
||||
this.props.changeCurrentLifecycleStatus(selectedStatus);
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Lifecycle state updated successfully!",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Lifecycle state updated successfully!',
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to add lifecycle");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to add lifecycle');
|
||||
this.setState({
|
||||
isConfirmButtonLoading: false
|
||||
isConfirmButtonLoading: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
const {currentStatus, selectedStatus, isConfirmButtonLoading} = this.state;
|
||||
const {lifecycle} = this.props;
|
||||
const {
|
||||
currentStatus,
|
||||
selectedStatus,
|
||||
isConfirmButtonLoading,
|
||||
} = this.state;
|
||||
const { lifecycle } = this.props;
|
||||
const selectedValue = selectedStatus == null ? [] : selectedStatus;
|
||||
let proceedingStates = [];
|
||||
|
||||
if (lifecycle !== null && (lifecycle.hasOwnProperty(currentStatus)) && lifecycle[currentStatus].hasOwnProperty("proceedingStates")) {
|
||||
if (
|
||||
lifecycle !== null &&
|
||||
lifecycle.hasOwnProperty(currentStatus) &&
|
||||
lifecycle[currentStatus].hasOwnProperty('proceedingStates')
|
||||
) {
|
||||
proceedingStates = lifecycle[currentStatus].proceedingStates;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title level={4}>Manage Lifecycle</Title>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<Paragraph>
|
||||
Ensure that your security policies are not violated by the application. Have a thorough review and
|
||||
approval process before directly publishing it to your app store. You can easily transition from one
|
||||
state to another. <br/>Note: ‘Change State To’ displays only the next states allowed from the
|
||||
Ensure that your security policies are not violated by the
|
||||
application. Have a thorough review and approval process before
|
||||
directly publishing it to your app store. You can easily transition
|
||||
from one state to another. <br />
|
||||
Note: ‘Change State To’ displays only the next states allowed from the
|
||||
current state
|
||||
</Paragraph>
|
||||
{lifecycle !== null && (<LifeCycleDetailsModal lifecycle={lifecycle}/>)}
|
||||
<Divider dashed={true}/>
|
||||
<Text strong={true}>Current State: </Text> <Tag color="blue">{currentStatus}</Tag><br/><br/>
|
||||
{lifecycle !== null && <LifeCycleDetailsModal lifecycle={lifecycle} />}
|
||||
<Divider dashed={true} />
|
||||
<Text strong={true}>Current State: </Text>{' '}
|
||||
<Tag color="blue">{currentStatus}</Tag>
|
||||
<br />
|
||||
<br />
|
||||
<Text>Change State to: </Text>
|
||||
<Select
|
||||
placeholder="Select state"
|
||||
style={{width: 120}}
|
||||
style={{ width: 120 }}
|
||||
size="small"
|
||||
onChange={this.handleSelectChange}
|
||||
value={selectedValue}
|
||||
@ -165,38 +196,39 @@ class LifeCycle extends React.Component {
|
||||
>
|
||||
{proceedingStates.map(lifecycleState => {
|
||||
return (
|
||||
<Option
|
||||
key={lifecycleState}
|
||||
value={lifecycleState}>
|
||||
<Option key={lifecycleState} value={lifecycleState}>
|
||||
{lifecycleState}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<Button
|
||||
style={{marginLeft: 10}}
|
||||
style={{ marginLeft: 10 }}
|
||||
size="small"
|
||||
type="primary"
|
||||
htmlType="button"
|
||||
onClick={this.showReasonModal}
|
||||
loading={isConfirmButtonLoading}
|
||||
disabled={selectedStatus == null}>
|
||||
disabled={selectedStatus == null}
|
||||
>
|
||||
Change
|
||||
</Button>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<Modal
|
||||
title="Confirm changing lifecycle state"
|
||||
visible={this.state.isReasonModalVisible}
|
||||
onOk={this.addLifeCycle}
|
||||
onCancel={this.closeReasonModal}
|
||||
okText="Confirm">
|
||||
okText="Confirm"
|
||||
>
|
||||
<Text>
|
||||
You are going to change the lifecycle state from,<br/>
|
||||
<Tag color="blue">{currentStatus}</Tag>to <Tag
|
||||
color="blue">{selectedStatus}</Tag>
|
||||
You are going to change the lifecycle state from,
|
||||
<br />
|
||||
<Tag color="blue">{currentStatus}</Tag>to{' '}
|
||||
<Tag color="blue">{selectedStatus}</Tag>
|
||||
</Text>
|
||||
<br/><br/>
|
||||
<br />
|
||||
<br />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={this.state.reasonText}
|
||||
@ -206,11 +238,9 @@ class LifeCycle extends React.Component {
|
||||
placeholder="Leave a comment (optional)"
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withConfigContext(LifeCycle);
|
||||
|
||||
@ -16,18 +16,17 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Modal, Button, Tag, List, Typography} from 'antd';
|
||||
import pSBC from "shade-blend-color";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import React from 'react';
|
||||
import { Modal, Button, Tag, List, Typography } from 'antd';
|
||||
import pSBC from 'shade-blend-color';
|
||||
import { withConfigContext } from '../../../../../context/ConfigContext';
|
||||
|
||||
const {Text} = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
class LifeCycleDetailsModal extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {visible: false};
|
||||
this.state = { visible: false };
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
@ -45,14 +44,10 @@ class LifeCycleDetailsModal extends React.Component {
|
||||
render() {
|
||||
const config = this.props.context;
|
||||
const lifeCycleConfig = config.lifecycle;
|
||||
const {lifecycle} = this.props;
|
||||
const { lifecycle } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
size="small"
|
||||
icon="question-circle"
|
||||
onClick={this.showModal}
|
||||
>
|
||||
<Button size="small" icon="question-circle" onClick={this.showModal}>
|
||||
Learn more
|
||||
</Button>
|
||||
<Modal
|
||||
@ -61,48 +56,47 @@ class LifeCycleDetailsModal extends React.Component {
|
||||
footer={null}
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={Object.keys(lifecycle)}
|
||||
renderItem={lifecycleState => {
|
||||
let text = "";
|
||||
let footerText = "";
|
||||
let text = '';
|
||||
let footerText = '';
|
||||
let nextProceedingStates = [];
|
||||
|
||||
if (lifeCycleConfig.hasOwnProperty(lifecycleState)) {
|
||||
text = lifeCycleConfig[lifecycleState].text;
|
||||
}
|
||||
if (lifecycle[lifecycleState].hasOwnProperty("proceedingStates")) {
|
||||
nextProceedingStates = lifecycle[lifecycleState].proceedingStates;
|
||||
footerText = "You can only proceed to one of the following states:"
|
||||
if (
|
||||
lifecycle[lifecycleState].hasOwnProperty('proceedingStates')
|
||||
) {
|
||||
nextProceedingStates =
|
||||
lifecycle[lifecycleState].proceedingStates;
|
||||
footerText =
|
||||
'You can only proceed to one of the following states:';
|
||||
}
|
||||
|
||||
return (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={lifecycleState}
|
||||
/>
|
||||
<List.Item.Meta title={lifecycleState} />
|
||||
{text}
|
||||
<br/>
|
||||
<br />
|
||||
<Text type="secondary">{footerText}</Text>
|
||||
<div>
|
||||
{
|
||||
nextProceedingStates.map(lifecycleState => {
|
||||
{nextProceedingStates.map(lifecycleState => {
|
||||
return (
|
||||
<Tag
|
||||
key={lifecycleState}
|
||||
style={{margin: 5}}
|
||||
color={pSBC(0.30, config.theme.primaryColor)}
|
||||
style={{ margin: 5 }}
|
||||
color={pSBC(0.3, config.theme.primaryColor)}
|
||||
>
|
||||
{lifecycleState}
|
||||
</Tag>
|
||||
)
|
||||
})
|
||||
}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</List.Item>
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
@ -16,15 +16,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {List, message, Avatar, Spin, Button, notification, Alert} from 'antd';
|
||||
import "./Reviews.css";
|
||||
import React from 'react';
|
||||
import { List, Spin, Button, Alert } from 'antd';
|
||||
import './Reviews.css';
|
||||
|
||||
import InfiniteScroll from 'react-infinite-scroller';
|
||||
import SingleReview from "./SingleReview";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
import SingleReview from './SingleReview';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
const limit = 5;
|
||||
|
||||
@ -35,11 +35,10 @@ class Reviews extends React.Component {
|
||||
hasMore: false,
|
||||
loadMore: false,
|
||||
forbiddenErrors: {
|
||||
reviews: false
|
||||
}
|
||||
reviews: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchData(0, limit, res => {
|
||||
this.setState({
|
||||
@ -51,39 +50,48 @@ class Reviews extends React.Component {
|
||||
fetchData = (offset, limit, callback) => {
|
||||
const config = this.props.context;
|
||||
|
||||
const {uuid, type} = this.props;
|
||||
const { uuid, type } = this.props;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
axios.get(
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
"/admin/reviews/" + type + "/" + uuid
|
||||
).then(res => {
|
||||
'/admin/reviews/' +
|
||||
type +
|
||||
'/' +
|
||||
uuid,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let reviews = res.data.data.data;
|
||||
callback(reviews);
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load reviews.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load reviews.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.reviews = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleInfiniteOnLoad = (count) => {
|
||||
handleInfiniteOnLoad = count => {
|
||||
const offset = count * limit;
|
||||
let data = this.state.data;
|
||||
this.setState({
|
||||
@ -106,7 +114,7 @@ class Reviews extends React.Component {
|
||||
} else {
|
||||
this.setState({
|
||||
hasMore: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -115,19 +123,20 @@ class Reviews extends React.Component {
|
||||
enableLoading = () => {
|
||||
this.setState({
|
||||
hasMore: true,
|
||||
loadMore: true
|
||||
loadMore: true,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{(this.state.forbiddenErrors.reviews) && (
|
||||
{this.state.forbiddenErrors.reviews && (
|
||||
<Alert
|
||||
message="You don't have permission to view reviews."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<div className="demo-infinite-container">
|
||||
<InfiniteScroll
|
||||
@ -135,24 +144,34 @@ class Reviews extends React.Component {
|
||||
pageStart={0}
|
||||
loadMore={this.handleInfiniteOnLoad}
|
||||
hasMore={!this.state.loading && this.state.hasMore}
|
||||
useWindow={true}>
|
||||
useWindow={true}
|
||||
>
|
||||
<List
|
||||
dataSource={this.state.data}
|
||||
renderItem={item => (
|
||||
<List.Item key={item.id}>
|
||||
<SingleReview review={item}/>
|
||||
<SingleReview review={item} />
|
||||
</List.Item>
|
||||
)}>
|
||||
)}
|
||||
>
|
||||
{this.state.loading && this.state.hasMore && (
|
||||
<div className="demo-loading-container">
|
||||
<Spin/>
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
</List>
|
||||
</InfiniteScroll>
|
||||
{!this.state.loadMore && (this.state.data.length >= limit) && (<div style={{textAlign: "center"}}>
|
||||
<Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
|
||||
</div>)}
|
||||
{!this.state.loadMore && this.state.data.length >= limit && (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Button
|
||||
type="dashed"
|
||||
htmlType="button"
|
||||
onClick={this.enableLoading}
|
||||
>
|
||||
Read All Reviews
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,32 +16,49 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Avatar} from "antd";
|
||||
import {List,Typography} from "antd";
|
||||
import StarRatings from "react-star-ratings";
|
||||
import React from 'react';
|
||||
import { Avatar } from 'antd';
|
||||
import { List, Typography } from 'antd';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
|
||||
const {Text, Paragraph} = Typography;
|
||||
const colorList = ['#f0932b','#badc58','#6ab04c','#eb4d4b','#0abde3', '#9b59b6','#3498db','#22a6b3'];
|
||||
const { Text, Paragraph } = Typography;
|
||||
const colorList = [
|
||||
'#f0932b',
|
||||
'#badc58',
|
||||
'#6ab04c',
|
||||
'#eb4d4b',
|
||||
'#0abde3',
|
||||
'#9b59b6',
|
||||
'#3498db',
|
||||
'#22a6b3',
|
||||
];
|
||||
|
||||
class SingleReview extends React.Component {
|
||||
|
||||
render() {
|
||||
const review = this.props.review;
|
||||
const randomColor = colorList[Math.floor(Math.random() * (colorList.length))];
|
||||
const randomColor = colorList[Math.floor(Math.random() * colorList.length)];
|
||||
const avatarLetter = review.username.charAt(0).toUpperCase();
|
||||
const content = (
|
||||
<div style={{marginTop: -5}}>
|
||||
<div style={{ marginTop: -5 }}>
|
||||
<StarRatings
|
||||
rating={review.rating}
|
||||
starRatedColor="#777"
|
||||
starDimension = "12px"
|
||||
starSpacing = "2px"
|
||||
starDimension="12px"
|
||||
starSpacing="2px"
|
||||
numberOfStars={5}
|
||||
name='rating'
|
||||
name="rating"
|
||||
/>
|
||||
<Text style={{fontSize: 12, color: "#aaa"}} type="secondary"> {review.createdAt}</Text><br/>
|
||||
<Paragraph ellipsis={{ rows: 3, expandable: true }} style={{color: "#777"}}>{review.content}</Paragraph>
|
||||
<Text style={{ fontSize: 12, color: '#aaa' }} type="secondary">
|
||||
{' '}
|
||||
{review.createdAt}
|
||||
</Text>
|
||||
<br />
|
||||
<Paragraph
|
||||
ellipsis={{ rows: 3, expandable: true }}
|
||||
style={{ color: '#777' }}
|
||||
>
|
||||
{review.content}
|
||||
</Paragraph>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -49,7 +66,10 @@ class SingleReview extends React.Component {
|
||||
<div>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar style={{ backgroundColor: randomColor, verticalAlign: 'middle' }} size="large">
|
||||
<Avatar
|
||||
style={{ backgroundColor: randomColor, verticalAlign: 'middle' }}
|
||||
size="large"
|
||||
>
|
||||
{avatarLetter}
|
||||
</Avatar>
|
||||
}
|
||||
|
||||
@ -16,35 +16,33 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, Divider, Form, Input, message, Modal, notification, Spin} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {withRouter} from "react-router";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
import React from 'react';
|
||||
import { Button, Divider, Input, Modal, notification, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import { withRouter } from 'react-router';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
class AddNewPage extends React.Component {
|
||||
|
||||
state = {
|
||||
visible: false,
|
||||
pageName: ''
|
||||
pageName: '',
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handlePageName = (e) => {
|
||||
handlePageName = e => {
|
||||
this.setState({
|
||||
pageName: e.target.value,
|
||||
});
|
||||
@ -52,41 +50,47 @@ class AddNewPage extends React.Component {
|
||||
|
||||
createNewPage = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/page',
|
||||
{
|
||||
"locale": "en",
|
||||
"pageName": this.state.pageName
|
||||
}
|
||||
).then(res => {
|
||||
locale: 'en',
|
||||
pageName: this.state.pageName,
|
||||
},
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const { pageId, pageName } = res.data.data;
|
||||
|
||||
const {pageId, pageName} = res.data.data;
|
||||
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Page created successfully!'
|
||||
description: 'Page created successfully!',
|
||||
});
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
|
||||
this.props.history.push(`/publisher/manage/android-enterprise/pages/${pageName}/${pageId}`);
|
||||
this.props.history.push(
|
||||
`/publisher/manage/android-enterprise/pages/${pageName}/${pageId}`,
|
||||
);
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the cluster.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{marginTop: 24, marginBottom: 24}}>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={this.showModal}>
|
||||
<div style={{ marginTop: 24, marginBottom: 24 }}>
|
||||
<Button type="dashed" onClick={this.showModal}>
|
||||
Add new page
|
||||
</Button>
|
||||
<Modal
|
||||
@ -99,18 +103,17 @@ class AddNewPage extends React.Component {
|
||||
>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<p>Choose a name for the page</p>
|
||||
<Input onChange={this.handlePageName}/>
|
||||
<Divider/>
|
||||
<Input onChange={this.handlePageName} />
|
||||
<Divider />
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Button onClick={this.handleCancel}>Cancel</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
onClick={this.createNewPage}
|
||||
htmlType="button" type="primary"
|
||||
disabled={this.state.pageName.length === 0}>
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
disabled={this.state.pageName.length === 0}
|
||||
>
|
||||
Create Page
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Modal, Button} from "antd";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import React from 'react';
|
||||
import { Modal, Button } from 'antd';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
|
||||
class GooglePlayIframe extends React.Component {
|
||||
constructor(props) {
|
||||
@ -26,7 +26,7 @@ class GooglePlayIframe extends React.Component {
|
||||
this.config = this.props.context;
|
||||
|
||||
this.state = {
|
||||
visible: false
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ class GooglePlayIframe extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{display: "inline-block", padding: 4}}>
|
||||
<div style={{ display: 'inline-block', padding: 4 }}>
|
||||
<Button type="primary" onClick={this.showModal}>
|
||||
Approve Applications
|
||||
</Button>
|
||||
@ -59,16 +59,20 @@ class GooglePlayIframe extends React.Component {
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
width = {740}
|
||||
footer={null}>
|
||||
width={740}
|
||||
footer={null}
|
||||
>
|
||||
<iframe
|
||||
style={{
|
||||
height: 720,
|
||||
border: 0,
|
||||
width: "100%"
|
||||
width: '100%',
|
||||
}}
|
||||
src={"https://play.google.com/work/embedded/search?token=" + this.config.androidEnterpriseToken +
|
||||
"&mode=APPROVE&showsearchbox=TRUE"}
|
||||
src={
|
||||
'https://play.google.com/work/embedded/search?token=' +
|
||||
this.config.androidEnterpriseToken +
|
||||
'&mode=APPROVE&showsearchbox=TRUE'
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
@ -16,22 +16,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, message, Modal, notification, Spin} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
|
||||
// import gapi from 'gapi-client';
|
||||
import React from 'react';
|
||||
import { Button, Modal, notification, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
class ManagedConfigurationsIframe extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.config = this.props.context;
|
||||
this.state = {
|
||||
visible: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -55,118 +52,152 @@ class ManagedConfigurationsIframe extends React.Component {
|
||||
};
|
||||
|
||||
getMcm = () => {
|
||||
const {packageName} = this.props;
|
||||
this.setState({loading: true});
|
||||
const { packageName } = this.props;
|
||||
this.setState({ loading: true });
|
||||
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + this.config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/managed-configs/package/" + packageName,
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
this.config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/managed-configs/package/' +
|
||||
packageName,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let mcmId = null;
|
||||
if (res.data.hasOwnProperty("data")) {
|
||||
if (res.data.hasOwnProperty('data')) {
|
||||
mcmId = res.data.data.mcmId;
|
||||
}
|
||||
this.loadIframe(mcmId);
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load configurations.");
|
||||
this.setState({loading: false, visible: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load configurations.',
|
||||
);
|
||||
this.setState({ loading: false, visible: false });
|
||||
});
|
||||
};
|
||||
|
||||
loadIframe = (mcmId) => {
|
||||
const {packageName} = this.props;
|
||||
let method = "post";
|
||||
loadIframe = mcmId => {
|
||||
const { packageName } = this.props;
|
||||
let method = 'post';
|
||||
// eslint-disable-next-line no-undef
|
||||
gapi.load('gapi.iframes', () => {
|
||||
const parameters = {
|
||||
token: this.config.androidEnterpriseToken,
|
||||
packageName: packageName
|
||||
packageName: packageName,
|
||||
};
|
||||
if (mcmId != null) {
|
||||
parameters.mcmId = mcmId;
|
||||
parameters.canDelete = true;
|
||||
method = "put";
|
||||
method = 'put';
|
||||
}
|
||||
|
||||
const queryString = Object.keys(parameters).map(key => key + '=' + parameters[key]).join('&');
|
||||
const queryString = Object.keys(parameters)
|
||||
.map(key => key + '=' + parameters[key])
|
||||
.join('&');
|
||||
|
||||
var options = {
|
||||
'url': "https://play.google.com/managed/mcm?" + queryString,
|
||||
'where': document.getElementById('manage-config-iframe-container'),
|
||||
'attributes': {style: 'height:720px', scrolling: 'yes'}
|
||||
url: 'https://play.google.com/managed/mcm?' + queryString,
|
||||
where: document.getElementById('manage-config-iframe-container'),
|
||||
attributes: { style: 'height:720px', scrolling: 'yes' },
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
var iframe = gapi.iframes.getContext().openChild(options);
|
||||
iframe.register('onconfigupdated', (event) => {
|
||||
iframe.register(
|
||||
'onconfigupdated',
|
||||
event => {
|
||||
this.updateConfig(method, event);
|
||||
}, gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER);
|
||||
},
|
||||
// eslint-disable-next-line no-undef
|
||||
gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER,
|
||||
);
|
||||
|
||||
iframe.register('onconfigdeleted', (event) => {
|
||||
iframe.register(
|
||||
'onconfigdeleted',
|
||||
event => {
|
||||
this.deleteConfig(event);
|
||||
}, gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER);
|
||||
},
|
||||
// eslint-disable-next-line no-undef
|
||||
gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
updateConfig = (method, event) => {
|
||||
const {packageName} = this.props;
|
||||
this.setState({loading: true});
|
||||
const { packageName } = this.props;
|
||||
this.setState({ loading: true });
|
||||
|
||||
const data = {
|
||||
mcmId: event.mcmId,
|
||||
profileName: event.name,
|
||||
packageName
|
||||
packageName,
|
||||
};
|
||||
|
||||
//send request to the invoker
|
||||
// send request to the invoker
|
||||
axios({
|
||||
method,
|
||||
url: window.location.origin + this.config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/managed-configs",
|
||||
data
|
||||
}).then(res => {
|
||||
url:
|
||||
window.location.origin +
|
||||
this.config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/managed-configs',
|
||||
data,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Configuration Profile updated Successfully',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update configurations.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update configurations.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
deleteConfig = (event) => {
|
||||
const {packageName} = this.props;
|
||||
this.setState({loading: true});
|
||||
deleteConfig = event => {
|
||||
this.setState({ loading: true });
|
||||
|
||||
//send request to the invoker
|
||||
axios.delete(
|
||||
window.location.origin + this.config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/managed-configs/mcm/" + event.mcmId
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.delete(
|
||||
window.location.origin +
|
||||
this.config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/managed-configs/mcm/' +
|
||||
event.mcmId,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Configuration Profile removed Successfully',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to remove configurations.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to remove configurations.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
@ -177,17 +208,18 @@ class ManagedConfigurationsIframe extends React.Component {
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="setting"
|
||||
onClick={this.showModal}>
|
||||
onClick={this.showModal}
|
||||
>
|
||||
Manage
|
||||
</Button>
|
||||
<Modal
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={null}>
|
||||
footer={null}
|
||||
>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<div id="manage-config-iframe-container">
|
||||
</div>
|
||||
<div id="manage-config-iframe-container"></div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
@ -16,28 +16,29 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Modal, Icon, Table, Avatar} from 'antd';
|
||||
import "../Cluster.css";
|
||||
import {withConfigContext} from "../../../../../../context/ConfigContext";
|
||||
import React from 'react';
|
||||
import { Modal, Icon, Table, Avatar } from 'antd';
|
||||
import '../Cluster.css';
|
||||
import { withConfigContext } from '../../../../../../context/ConfigContext';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'iconUrl',
|
||||
key: 'iconUrl',
|
||||
render: (iconUrl) => (<Avatar shape="square" src={iconUrl}/>)
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: iconUrl => <Avatar shape="square" src={iconUrl} />,
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Page',
|
||||
dataIndex: 'packageId',
|
||||
key: 'packageId'
|
||||
}
|
||||
key: 'packageId',
|
||||
},
|
||||
];
|
||||
|
||||
class AddAppsToClusterModal extends React.Component {
|
||||
@ -47,7 +48,7 @@ class AddAppsToClusterModal extends React.Component {
|
||||
visible: false,
|
||||
loading: false,
|
||||
selectedProducts: [],
|
||||
homePageId: null
|
||||
homePageId: null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -71,32 +72,30 @@ class AddAppsToClusterModal extends React.Component {
|
||||
rowSelection = {
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
this.setState({
|
||||
selectedProducts: selectedRows
|
||||
})
|
||||
selectedProducts: selectedRows,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
const {pagination, loading} = this.state;
|
||||
const { pagination, loading } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<div className="btn-add-new-wrapper">
|
||||
<div className="btn-add-new">
|
||||
<button className="btn"
|
||||
onClick={this.showModal}>
|
||||
<Icon style={{position: "relative"}} type="plus"/>
|
||||
<button className="btn" onClick={this.showModal}>
|
||||
<Icon style={{ position: 'relative' }} type="plus" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="title">
|
||||
Add app
|
||||
</div>
|
||||
<div className="title">Add app</div>
|
||||
</div>
|
||||
<Modal
|
||||
title="Select Apps"
|
||||
width={640}
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}>
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey={record => record.packageId}
|
||||
@ -104,10 +103,11 @@ class AddAppsToClusterModal extends React.Component {
|
||||
scroll={{ x: 300 }}
|
||||
pagination={{
|
||||
...pagination,
|
||||
size: "small",
|
||||
size: 'small',
|
||||
// position: "top",
|
||||
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} pages`,
|
||||
showQuickJumper: true
|
||||
showTotal: (total, range) =>
|
||||
`showing ${range[0]}-${range[1]} of ${total} pages`,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
loading={loading}
|
||||
onChange={this.handleTableChange}
|
||||
|
||||
@ -16,46 +16,57 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, Col, Divider, Icon, message, notification, Popconfirm, Row, Spin, Tooltip, Typography} from "antd";
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Divider,
|
||||
Icon,
|
||||
message,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Spin,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
|
||||
import "./Cluster.css";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import AddAppsToClusterModal from "./AddAppsToClusterModal/AddAppsToClusterModal";
|
||||
import {handleApiError} from "../../../../../js/Utils";
|
||||
import './Cluster.css';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../context/ConfigContext';
|
||||
import AddAppsToClusterModal from './AddAppsToClusterModal/AddAppsToClusterModal';
|
||||
import { handleApiError } from '../../../../../js/Utils';
|
||||
|
||||
const {Title} = Typography;
|
||||
const { Title } = Typography;
|
||||
|
||||
class Cluster extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const {cluster, pageId} = this.props;
|
||||
const { cluster, pageId } = this.props;
|
||||
this.originalCluster = Object.assign({}, cluster);
|
||||
const {name, products, clusterId} = cluster;
|
||||
const { name, products, clusterId } = cluster;
|
||||
this.clusterId = clusterId;
|
||||
this.pageId = pageId;
|
||||
this.state = {
|
||||
name,
|
||||
products,
|
||||
isSaveable: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleNameChange = (name) => {
|
||||
handleNameChange = name => {
|
||||
this.setState({
|
||||
name
|
||||
name,
|
||||
});
|
||||
if (name !== this.originalCluster.name) {
|
||||
this.setState({
|
||||
isSaveable: true
|
||||
isSaveable: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
isProductsChanged = (currentProducts) => {
|
||||
isProductsChanged = currentProducts => {
|
||||
let isChanged = false;
|
||||
const originalProducts = this.originalCluster.products;
|
||||
if (currentProducts.length === originalProducts.length) {
|
||||
@ -75,48 +86,50 @@ class Cluster extends React.Component {
|
||||
const products = [...this.state.products];
|
||||
if (swapIndex !== -1 && index < products.length) {
|
||||
// swap elements
|
||||
[products[index], products[swapIndex]] = [products[swapIndex], products[index]];
|
||||
[products[index], products[swapIndex]] = [
|
||||
products[swapIndex],
|
||||
products[index],
|
||||
];
|
||||
|
||||
this.setState({
|
||||
products,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
isSaveable: this.isProductsChanged(products)
|
||||
})
|
||||
isSaveable: this.isProductsChanged(products),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
removeProduct = (index) => {
|
||||
removeProduct = index => {
|
||||
const products = [...this.state.products];
|
||||
products.splice(index, 1);
|
||||
this.setState({
|
||||
products,
|
||||
isSaveable: true
|
||||
isSaveable: true,
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
getCurrentCluster = () => {
|
||||
const {products, name} = this.state;
|
||||
const { products, name } = this.state;
|
||||
return {
|
||||
pageId: this.pageId,
|
||||
clusterId: this.clusterId,
|
||||
name: name,
|
||||
products: products,
|
||||
orderInPage: this.props.orderInPage
|
||||
orderInPage: this.props.orderInPage,
|
||||
};
|
||||
};
|
||||
|
||||
resetChanges = () => {
|
||||
const cluster = this.originalCluster;
|
||||
const {name, products} = cluster;
|
||||
const { name, products } = cluster;
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
name,
|
||||
products,
|
||||
isSaveable: false
|
||||
isSaveable: false,
|
||||
});
|
||||
};
|
||||
|
||||
@ -124,20 +137,22 @@ class Cluster extends React.Component {
|
||||
const config = this.props.context;
|
||||
|
||||
const cluster = this.getCurrentCluster();
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/cluster",
|
||||
cluster
|
||||
).then(res => {
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/cluster',
|
||||
cluster,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Cluster updated successfully!'
|
||||
description: 'Cluster updated successfully!',
|
||||
});
|
||||
const cluster = res.data.data;
|
||||
const {name, products} = cluster;
|
||||
|
||||
this.originalCluster = Object.assign({}, cluster);
|
||||
|
||||
@ -146,25 +161,32 @@ class Cluster extends React.Component {
|
||||
this.props.toggleAddNewClusterVisibility(false);
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the cluster.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
deleteCluster = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
axios.delete(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
`/device-mgt/android/v1.0/enterprise/store-layout/cluster/${this.clusterId}/page/${this.pageId}`
|
||||
).then(res => {
|
||||
axios
|
||||
.delete(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
`/device-mgt/android/v1.0/enterprise/store-layout/cluster/${this.clusterId}/page/` +
|
||||
this.pageId,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Cluster deleted successfully!'
|
||||
description: 'Cluster deleted successfully!',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
@ -172,24 +194,26 @@ class Cluster extends React.Component {
|
||||
});
|
||||
|
||||
this.props.removeLoadedCluster(this.clusterId);
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the cluster.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
getUnselectedProducts = () => {
|
||||
const {applications} = this.props;
|
||||
const { applications } = this.props;
|
||||
const selectedProducts = this.state.products;
|
||||
|
||||
// get a copy from all products
|
||||
const unSelectedProducts = [...applications];
|
||||
|
||||
// remove selected products from unselected products
|
||||
selectedProducts.forEach((selectedProduct) => {
|
||||
selectedProducts.forEach(selectedProduct => {
|
||||
for (let i = 0; i < unSelectedProducts.length; i++) {
|
||||
if (selectedProduct.packageId === unSelectedProducts[i].packageId) {
|
||||
// remove item from array
|
||||
@ -201,10 +225,10 @@ class Cluster extends React.Component {
|
||||
return unSelectedProducts;
|
||||
};
|
||||
|
||||
addSelectedProducts = (products) => {
|
||||
addSelectedProducts = products => {
|
||||
this.setState({
|
||||
products: [...this.state.products, ...products],
|
||||
isSaveable: products.length > 0
|
||||
isSaveable: products.length > 0,
|
||||
});
|
||||
};
|
||||
|
||||
@ -217,17 +241,20 @@ class Cluster extends React.Component {
|
||||
const config = this.props.context;
|
||||
|
||||
const cluster = this.getCurrentCluster();
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/cluster",
|
||||
cluster
|
||||
).then(res => {
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/cluster',
|
||||
cluster,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Cluster updated successfully!'
|
||||
description: 'Cluster updated successfully!',
|
||||
});
|
||||
|
||||
const cluster = res.data.data;
|
||||
@ -235,33 +262,33 @@ class Cluster extends React.Component {
|
||||
this.resetChanges();
|
||||
this.props.addSavedClusterToThePage(cluster);
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to update the cluster.",
|
||||
description: 'Error occurred while trying to update the cluster.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {name, products, loading} = this.state;
|
||||
const { name, products, loading } = this.state;
|
||||
const unselectedProducts = this.getUnselectedProducts();
|
||||
const {isTemporary, index} = this.props;
|
||||
const Product = ({product, index}) => {
|
||||
const {packageId} = product;
|
||||
let imageSrc = "";
|
||||
const { isTemporary, index } = this.props;
|
||||
const Product = ({ product, index }) => {
|
||||
const { packageId } = product;
|
||||
let imageSrc = '';
|
||||
const iconUrl = product.iconUrl;
|
||||
// check if the icon url is an url or google image id
|
||||
if (iconUrl.startsWith("http")) {
|
||||
if (iconUrl.startsWith('http')) {
|
||||
imageSrc = iconUrl;
|
||||
} else {
|
||||
imageSrc = `https://lh3.googleusercontent.com/${iconUrl}=s240-rw`;
|
||||
@ -269,19 +296,20 @@ class Cluster extends React.Component {
|
||||
return (
|
||||
<div className="product">
|
||||
<div className="arrow">
|
||||
<button disabled={index === 0} className="btn"
|
||||
<button
|
||||
disabled={index === 0}
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
this.swapProduct(index, index - 1);
|
||||
}}>
|
||||
<Icon type="caret-left" theme="filled"/>
|
||||
}}
|
||||
>
|
||||
<Icon type="caret-left" theme="filled" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="product-icon">
|
||||
<img src={imageSrc}/>
|
||||
<img src={imageSrc} />
|
||||
<Tooltip title={packageId}>
|
||||
<div className="title">
|
||||
{packageId}
|
||||
</div>
|
||||
<div className="title">{packageId}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="arrow">
|
||||
@ -289,14 +317,20 @@ class Cluster extends React.Component {
|
||||
disabled={index === products.length - 1}
|
||||
onClick={() => {
|
||||
this.swapProduct(index, index + 1);
|
||||
}} className="btn btn-right"><Icon type="caret-right" theme="filled"/></button>
|
||||
}}
|
||||
className="btn btn-right"
|
||||
>
|
||||
<Icon type="caret-right" theme="filled" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="delete-btn">
|
||||
<button className="btn"
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
this.removeProduct(index)
|
||||
}}>
|
||||
<Icon type="close-circle" theme="filled"/>
|
||||
this.removeProduct(index);
|
||||
}}
|
||||
>
|
||||
<Icon type="close-circle" theme="filled" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -308,19 +342,23 @@ class Cluster extends React.Component {
|
||||
<Spin spinning={loading}>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<Title editable={{onChange: this.handleNameChange}} level={4}>{name}</Title>
|
||||
<Title editable={{ onChange: this.handleNameChange }} level={4}>
|
||||
{name}
|
||||
</Title>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isTemporary && (
|
||||
<div style={{float: "right"}}>
|
||||
<div style={{ float: 'right' }}>
|
||||
<Tooltip title="Move Up">
|
||||
<Button
|
||||
type="link"
|
||||
icon="caret-up"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.props.swapClusters(index, index - 1)
|
||||
}} htmlType="button"/>
|
||||
this.props.swapClusters(index, index - 1);
|
||||
}}
|
||||
htmlType="button"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Move Down">
|
||||
<Button
|
||||
@ -328,20 +366,24 @@ class Cluster extends React.Component {
|
||||
icon="caret-down"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.props.swapClusters(index, index + 1)
|
||||
}} htmlType="button"/>
|
||||
this.props.swapClusters(index, index + 1);
|
||||
}}
|
||||
htmlType="button"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete Cluster">
|
||||
<Popconfirm
|
||||
title="Are you sure?"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onConfirm={this.deleteCluster}>
|
||||
onConfirm={this.deleteCluster}
|
||||
>
|
||||
<Button
|
||||
type="danger"
|
||||
icon="delete"
|
||||
shape="circle"
|
||||
htmlType="button"/>
|
||||
htmlType="button"
|
||||
/>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -351,33 +393,37 @@ class Cluster extends React.Component {
|
||||
<div className="products-row">
|
||||
<AddAppsToClusterModal
|
||||
addSelectedProducts={this.addSelectedProducts}
|
||||
unselectedProducts={unselectedProducts}/>
|
||||
{
|
||||
products.map((product, index) => {
|
||||
unselectedProducts={unselectedProducts}
|
||||
/>
|
||||
{products.map((product, index) => {
|
||||
return (
|
||||
<Product
|
||||
key={product.packageId}
|
||||
product={product}
|
||||
index={index}/>
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
<Row>
|
||||
<Col>
|
||||
{isTemporary && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.cancelAddingNewCluster}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Button onClick={this.cancelAddingNewCluster}>Cancel</Button>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip
|
||||
title={(products.length === 0) ? "You must add applications to the cluster before saving" : ""}>
|
||||
title={
|
||||
products.length === 0
|
||||
? 'You must add applications to the cluster before saving'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={products.length === 0}
|
||||
onClick={this.saveNewCluster}
|
||||
htmlType="button" type="primary">
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@ -387,14 +433,17 @@ class Cluster extends React.Component {
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.resetChanges}
|
||||
disabled={!this.state.isSaveable}>
|
||||
disabled={!this.state.isSaveable}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
onClick={this.updateCluster}
|
||||
htmlType="button" type="primary"
|
||||
disabled={!this.state.isSaveable}>
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
disabled={!this.state.isSaveable}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -16,32 +16,30 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, message, Modal, notification, Select, Spin} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../../js/Utils";
|
||||
import React from 'react';
|
||||
import { Button, Modal, notification, Select, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../../../js/Utils';
|
||||
|
||||
const {Option} = Select;
|
||||
const { Option } = Select;
|
||||
|
||||
class EditLinks extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.selectedLinks = [];
|
||||
this.state = {
|
||||
visible: false
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
@ -50,63 +48,72 @@ class EditLinks extends React.Component {
|
||||
|
||||
updateLinks = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page-link",
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/page-link',
|
||||
{
|
||||
pageId: this.props.pageId,
|
||||
links: this.selectedLinks
|
||||
}
|
||||
).then(res => {
|
||||
links: this.selectedLinks,
|
||||
},
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Links updated successfully!'
|
||||
description: 'Links updated successfully!',
|
||||
});
|
||||
|
||||
this.props.updateLinks(this.selectedLinks);
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the cluster.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
handleChange= (selectedLinks) =>{
|
||||
handleChange = selectedLinks => {
|
||||
this.selectedLinks = selectedLinks;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={this.showModal} type="link">[add / remove links]</Button>
|
||||
<Button onClick={this.showModal} type="link">
|
||||
[add / remove links]
|
||||
</Button>
|
||||
<Modal
|
||||
title="Add / Remove Links"
|
||||
visible={this.state.visible}
|
||||
onOk={this.updateLinks}
|
||||
onCancel={this.handleCancel}
|
||||
okText="Update">
|
||||
okText="Update"
|
||||
>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Please select links"
|
||||
defaultValue={this.props.selectedLinks}
|
||||
onChange={this.handleChange}>
|
||||
{
|
||||
this.props.pages.map((page) => (
|
||||
<Option disabled={page.id===this.props.pageId} key={page.id}>
|
||||
{page.name[0]["text"]}
|
||||
</Option>))
|
||||
}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
{this.props.pages.map(page => (
|
||||
<Option disabled={page.id === this.props.pageId} key={page.id}>
|
||||
{page.name[0].text}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Spin>
|
||||
</Modal>
|
||||
|
||||
@ -16,41 +16,45 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import axios from "axios";
|
||||
import {Tag, message, notification, Table, Typography, Divider, Icon, Popconfirm, Button} from "antd";
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
Tag,
|
||||
notification,
|
||||
Table,
|
||||
Typography,
|
||||
Divider,
|
||||
Icon,
|
||||
Popconfirm,
|
||||
Button,
|
||||
} from 'antd';
|
||||
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import "./Pages.css";
|
||||
import {Link} from "react-router-dom";
|
||||
import AddNewPage from "../AddNewPage/AddNewPage";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
|
||||
const {Text, Title} = Typography;
|
||||
|
||||
let config = null;
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import './Pages.css';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AddNewPage from '../AddNewPage/AddNewPage';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
class Pages extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
config = this.props.context;
|
||||
// TimeAgo.addLocale(en);
|
||||
this.state = {
|
||||
data: [],
|
||||
pagination: {},
|
||||
loading: false,
|
||||
selectedRows: [],
|
||||
homePageId: null
|
||||
homePageId: null,
|
||||
};
|
||||
}
|
||||
|
||||
rowSelection = {
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
this.setState({
|
||||
selectedRows: selectedRows
|
||||
})
|
||||
}
|
||||
selectedRows: selectedRows,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -58,124 +62,136 @@ class Pages extends React.Component {
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
//fetch data from api
|
||||
// fetch data from api
|
||||
fetch = (params = {}) => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
// get current page
|
||||
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
|
||||
this.setState({ loading: true });
|
||||
|
||||
const extraParams = {
|
||||
offset: 10 * (currentPage - 1), //calculate the offset
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/page',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const pagination = {...this.state.pagination};
|
||||
const pagination = { ...this.state.pagination };
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: res.data.data.page,
|
||||
pagination,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load pages.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to load pages.');
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
setHomePage = () => {
|
||||
const config = this.props.context;
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/home-page",
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/home-page',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
this.setState({
|
||||
homePageId: res.data.data.homepageId
|
||||
homePageId: res.data.data.homepageId,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to get home page.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to get home page.');
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
updateHomePage = (pageId) => {
|
||||
updateHomePage = pageId => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
//send request to the invoker
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/home-page/" + pageId,
|
||||
{}
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/home-page/' +
|
||||
pageId,
|
||||
{},
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Home page was updated successfully!",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Home page was updated successfully!',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
homePageId: res.data.data.homepageId,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the home page.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the home page.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
deletePage = (pageId) => {
|
||||
const {data} = this.state;
|
||||
deletePage = pageId => {
|
||||
const { data } = this.state;
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
//send request to the invoker
|
||||
axios.delete(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page/" + pageId
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.delete(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/page/' +
|
||||
pageId,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Home page was updated successfully!",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Home page was updated successfully!',
|
||||
});
|
||||
|
||||
for( let i = 0; i < data.length; i++){
|
||||
if ( data[i].id === pageId) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].id === pageId) {
|
||||
data.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to delete the page.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to delete the page.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
const pager = {...this.state.pagination};
|
||||
const pager = { ...this.state.pagination };
|
||||
pager.current = pagination.current;
|
||||
this.setState({
|
||||
pagination: pager,
|
||||
@ -190,12 +206,20 @@ class Pages extends React.Component {
|
||||
width: 300,
|
||||
render: (name, page) => {
|
||||
const pageName = name[0].text;
|
||||
return (<div>
|
||||
<Link to={`/publisher/manage/android-enterprise/pages/${pageName}/${page.id}`}> {pageName + " "}</Link>
|
||||
{(page.id === this.state.homePageId) && (<Tag color="#badc58">Home Page</Tag>)}
|
||||
</div>)
|
||||
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
to={`/publisher/manage/android-enterprise/pages/${pageName}/${page.id}`}
|
||||
>
|
||||
{' '}
|
||||
{pageName + ' '}
|
||||
</Link>
|
||||
{page.id === this.state.homePageId && (
|
||||
<Tag color="#badc58">Home Page</Tag>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Actions',
|
||||
@ -203,26 +227,31 @@ class Pages extends React.Component {
|
||||
render: (name, page) => (
|
||||
<div>
|
||||
<span className="action">
|
||||
<Button disabled={page.id === this.state.homePageId}
|
||||
<Button
|
||||
disabled={page.id === this.state.homePageId}
|
||||
className="btn-warning"
|
||||
icon="home"
|
||||
type="link"
|
||||
onClick={() => {
|
||||
this.updateHomePage(page.id);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
set as homepage
|
||||
</Button>
|
||||
</span>
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
<Popconfirm
|
||||
title="Are you sure?"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onConfirm={() => {
|
||||
this.deletePage(page.id);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<span className="action">
|
||||
<Text type="danger"><Icon type="delete"/> delete</Text>
|
||||
<Text type="danger">
|
||||
<Icon type="delete" /> delete
|
||||
</Text>
|
||||
</span>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
@ -231,27 +260,28 @@ class Pages extends React.Component {
|
||||
];
|
||||
|
||||
render() {
|
||||
const {data, pagination, loading, selectedRows} = this.state;
|
||||
const { data, pagination, loading } = this.state;
|
||||
return (
|
||||
<div className="layout-pages">
|
||||
<Title level={4}>Pages</Title>
|
||||
<AddNewPage/>
|
||||
<div style={{backgroundColor: "#ffffff", borderRadius: 5}}>
|
||||
<AddNewPage />
|
||||
<div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
|
||||
<Table
|
||||
columns={this.columns}
|
||||
rowKey={record => record.id}
|
||||
dataSource={data}
|
||||
pagination={{
|
||||
...pagination,
|
||||
size: "small",
|
||||
size: 'small',
|
||||
// position: "top",
|
||||
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} pages`,
|
||||
showQuickJumper: true
|
||||
showTotal: (total, range) =>
|
||||
`showing ${range[0]}-${range[1]} of ${total} pages`,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
loading={loading}
|
||||
onChange={this.handleTableChange}
|
||||
// rowSelection={this.rowSelection}
|
||||
scroll={{x: 1000}}
|
||||
scroll={{ x: 1000 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -16,63 +16,65 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, notification} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
import React from 'react';
|
||||
import { Button, notification } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../js/Utils';
|
||||
|
||||
class SyncAndroidApps extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false
|
||||
}
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
syncApps = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + "/device-mgt/android/v1.0/enterprise/products/sync",
|
||||
).then(res => {
|
||||
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Apps synced successfully!",
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/products/sync',
|
||||
)
|
||||
.then(res => {
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Apps synced successfully!',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while syncing the apps.");
|
||||
this.setState({
|
||||
loading: false
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while syncing the apps.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {loading} = this.state;
|
||||
const { loading } = this.state;
|
||||
return (
|
||||
<div style={{display: "inline-block", padding: 4}}>
|
||||
<div style={{ display: 'inline-block', padding: 4 }}>
|
||||
<Button
|
||||
onClick={this.syncApps}
|
||||
loading={loading}
|
||||
style={{marginTop: 16}}
|
||||
style={{ marginTop: 16 }}
|
||||
type="primary"
|
||||
icon="sync"
|
||||
>
|
||||
Sync{loading && "ing..."}
|
||||
Sync{loading && 'ing...'}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
Card,
|
||||
Tag,
|
||||
@ -32,15 +32,16 @@ import {
|
||||
Modal,
|
||||
Row,
|
||||
Col,
|
||||
Typography, Alert
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import {TweenOneGroup} from 'rc-tween-one';
|
||||
import pSBC from "shade-blend-color";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
Typography,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import { TweenOneGroup } from 'rc-tween-one';
|
||||
import pSBC from 'shade-blend-color';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../js/Utils';
|
||||
|
||||
const {Title} = Typography;
|
||||
const { Title } = Typography;
|
||||
|
||||
class ManageCategories extends React.Component {
|
||||
state = {
|
||||
@ -55,35 +56,44 @@ class ManageCategories extends React.Component {
|
||||
currentlyEditingId: null,
|
||||
editingValue: null,
|
||||
forbiddenErrors: {
|
||||
categories: false
|
||||
}
|
||||
categories: false,
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories",
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/categories',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let categories = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
categories: categories,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occured while trying to load categories", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occured while trying to load categories',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.categories = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -92,60 +102,71 @@ class ManageCategories extends React.Component {
|
||||
handleCloseButton = () => {
|
||||
this.setState({
|
||||
tempElements: [],
|
||||
isAddNewVisible: false
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
deleteCategory = (id) => {
|
||||
deleteCategory = id => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
axios.delete(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories/" + id,
|
||||
).then(res => {
|
||||
axios
|
||||
.delete(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/categories/' +
|
||||
id,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Category Removed Successfully!",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Category Removed Successfully!',
|
||||
});
|
||||
|
||||
const {categories} = this.state;
|
||||
const remainingElements = categories.filter(function (value) {
|
||||
const { categories } = this.state;
|
||||
const remainingElements = categories.filter(function(value) {
|
||||
return value.categoryName !== id;
|
||||
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
categories: remainingElements
|
||||
categories: remainingElements,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load categories.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
);
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
renderElement = (category) => {
|
||||
renderElement = category => {
|
||||
const config = this.props.context;
|
||||
const categoryName = category.categoryName;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
color={pSBC(0.30, config.theme.primaryColor)}
|
||||
style={{marginTop: 8}}>
|
||||
color={pSBC(0.3, config.theme.primaryColor)}
|
||||
style={{ marginTop: 8 }}
|
||||
>
|
||||
{categoryName}
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="edit">
|
||||
<Icon onClick={() => {
|
||||
this.openEditModal(categoryName)
|
||||
}} type="edit"/>
|
||||
<Icon
|
||||
onClick={() => {
|
||||
this.openEditModal(categoryName);
|
||||
}}
|
||||
type="edit"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="delete">
|
||||
<Popconfirm
|
||||
title="Are you sure delete this category?"
|
||||
@ -153,43 +174,41 @@ class ManageCategories extends React.Component {
|
||||
if (category.isCategoryDeletable) {
|
||||
this.deleteCategory(categoryName);
|
||||
} else {
|
||||
notification["error"]({
|
||||
notification.error({
|
||||
message: 'Cannot delete "' + categoryName + '"',
|
||||
description:
|
||||
"This category is currently used. Please unassign the category from apps.",
|
||||
'This category is currently used. Please unassign the category from apps.',
|
||||
});
|
||||
}
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No">
|
||||
<Icon type="delete"/>
|
||||
cancelText="No"
|
||||
>
|
||||
<Icon type="delete" />
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={category.categoryName} style={{display: 'inline-block'}}>
|
||||
<span key={category.categoryName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
renderTempElement = (category) => {
|
||||
const config = this.props.context;
|
||||
renderTempElement = category => {
|
||||
const tagElem = (
|
||||
<Tag
|
||||
style={{marginTop: 8}}
|
||||
style={{ marginTop: 8 }}
|
||||
closable
|
||||
onClose={e => {
|
||||
e.preventDefault();
|
||||
const {tempElements} = this.state;
|
||||
const remainingElements = tempElements.filter(function (value) {
|
||||
|
||||
const { tempElements } = this.state;
|
||||
const remainingElements = tempElements.filter(function(value) {
|
||||
return value.categoryName !== category.categoryName;
|
||||
|
||||
});
|
||||
this.setState({
|
||||
tempElements: remainingElements
|
||||
tempElements: remainingElements,
|
||||
});
|
||||
}}
|
||||
>
|
||||
@ -197,26 +216,32 @@ class ManageCategories extends React.Component {
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={category.categoryName} style={{display: 'inline-block'}}>
|
||||
<span key={category.categoryName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
showInput = () => {
|
||||
this.setState({inputVisible: true}, () => this.input.focus());
|
||||
this.setState({ inputVisible: true }, () => this.input.focus());
|
||||
};
|
||||
|
||||
handleInputChange = e => {
|
||||
this.setState({inputValue: e.target.value});
|
||||
this.setState({ inputValue: e.target.value });
|
||||
};
|
||||
|
||||
handleInputConfirm = () => {
|
||||
const {inputValue, categories} = this.state;
|
||||
let {tempElements} = this.state;
|
||||
const { inputValue, categories } = this.state;
|
||||
let { tempElements } = this.state;
|
||||
if (inputValue) {
|
||||
if ((categories.findIndex(i => i.categoryName === inputValue) === -1) && (tempElements.findIndex(i => i.categoryName === inputValue) === -1)) {
|
||||
tempElements = [...tempElements, {categoryName: inputValue, isCategoryDeletable: true}];
|
||||
if (
|
||||
categories.findIndex(i => i.categoryName === inputValue) === -1 &&
|
||||
tempElements.findIndex(i => i.categoryName === inputValue) === -1
|
||||
) {
|
||||
tempElements = [
|
||||
...tempElements,
|
||||
{ categoryName: inputValue, isCategoryDeletable: true },
|
||||
];
|
||||
} else {
|
||||
message.warning('Category already exists');
|
||||
}
|
||||
@ -231,22 +256,26 @@ class ManageCategories extends React.Component {
|
||||
|
||||
handleSave = () => {
|
||||
const config = this.props.context;
|
||||
const {tempElements, categories} = this.state;
|
||||
const { tempElements, categories } = this.state;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const data = tempElements.map(category => category.categoryName);
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories",
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/categories',
|
||||
data,
|
||||
).then(res => {
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"New Categories were added successfully",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'New Categories were added successfully',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
@ -255,18 +284,16 @@ class ManageCategories extends React.Component {
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
loading: false,
|
||||
isAddNewVisible: false
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to add categories.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to add categories.');
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
saveInputRef = input => (this.input = input);
|
||||
@ -274,77 +301,95 @@ class ManageCategories extends React.Component {
|
||||
closeEditModal = e => {
|
||||
this.setState({
|
||||
isEditModalVisible: false,
|
||||
currentlyEditingId: null
|
||||
currentlyEditingId: null,
|
||||
});
|
||||
};
|
||||
|
||||
openEditModal = (id) => {
|
||||
openEditModal = id => {
|
||||
this.setState({
|
||||
isEditModalVisible: true,
|
||||
currentlyEditingId: id,
|
||||
editingValue: id
|
||||
})
|
||||
editingValue: id,
|
||||
});
|
||||
};
|
||||
|
||||
editItem = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
const {editingValue, currentlyEditingId, categories} = this.state;
|
||||
const { editingValue, currentlyEditingId, categories } = this.state;
|
||||
|
||||
this.setState({
|
||||
loading: true,
|
||||
isEditModalVisible: false,
|
||||
});
|
||||
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories/rename?from=" + currentlyEditingId + "&to=" + editingValue,
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/categories/rename?from=' +
|
||||
currentlyEditingId +
|
||||
'&to=' +
|
||||
editingValue,
|
||||
{},
|
||||
).then(res => {
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Category was edited successfully",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Category was edited successfully',
|
||||
});
|
||||
|
||||
categories[categories.findIndex(i => i.categoryName === currentlyEditingId)].categoryName = editingValue;
|
||||
categories[
|
||||
categories.findIndex(i => i.categoryName === currentlyEditingId)
|
||||
].categoryName = editingValue;
|
||||
|
||||
this.setState({
|
||||
categories: categories,
|
||||
loading: false,
|
||||
editingValue: null
|
||||
editingValue: null,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to delete the category.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to delete the category.',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
editingValue: null
|
||||
editingValue: null,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
handleEditInputChange = (e) => {
|
||||
handleEditInputChange = e => {
|
||||
this.setState({
|
||||
editingValue: e.target.value
|
||||
editingValue: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {categories, inputVisible, inputValue, tempElements, isAddNewVisible, forbiddenErrors} = this.state;
|
||||
const {
|
||||
categories,
|
||||
inputVisible,
|
||||
inputValue,
|
||||
tempElements,
|
||||
isAddNewVisible,
|
||||
forbiddenErrors,
|
||||
} = this.state;
|
||||
const categoriesElements = categories.map(this.renderElement);
|
||||
const temporaryElements = tempElements.map(this.renderTempElement);
|
||||
return (
|
||||
<div style={{marginBottom: 16}}>
|
||||
{(forbiddenErrors.categories) && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
{forbiddenErrors.categories && (
|
||||
<Alert
|
||||
message="You don't have permission to view categories."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Card>
|
||||
<Spin tip="Working on it..." spinning={this.state.loading}>
|
||||
@ -353,27 +398,33 @@ class ManageCategories extends React.Component {
|
||||
<Title level={4}>Categories</Title>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isAddNewVisible &&
|
||||
<div style={{float: "right"}}>
|
||||
{!isAddNewVisible && (
|
||||
<div style={{ float: 'right' }}>
|
||||
<Button
|
||||
icon="plus"
|
||||
// type="primary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
this.setState(
|
||||
{
|
||||
isAddNewVisible: true,
|
||||
inputVisible: true
|
||||
}, () => this.input.focus())
|
||||
}} htmlType="button">Add
|
||||
inputVisible: true,
|
||||
},
|
||||
() => this.input.focus(),
|
||||
);
|
||||
}}
|
||||
htmlType="button"
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
{isAddNewVisible &&
|
||||
{isAddNewVisible && (
|
||||
<div>
|
||||
<Divider/>
|
||||
<div style={{marginBottom: 16}}>
|
||||
<Divider />
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
@ -384,7 +435,7 @@ class ManageCategories extends React.Component {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{temporaryElements}
|
||||
@ -394,7 +445,7 @@ class ManageCategories extends React.Component {
|
||||
ref={this.saveInputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
style={{width: 120}}
|
||||
style={{ width: 120 }}
|
||||
value={inputValue}
|
||||
onChange={this.handleInputChange}
|
||||
onBlur={this.handleInputConfirm}
|
||||
@ -402,9 +453,11 @@ class ManageCategories extends React.Component {
|
||||
/>
|
||||
)}
|
||||
{!inputVisible && (
|
||||
<Tag onClick={this.showInput}
|
||||
style={{background: '#fff', borderStyle: 'dashed'}}>
|
||||
<Icon type="plus"/> New Category
|
||||
<Tag
|
||||
onClick={this.showInput}
|
||||
style={{ background: '#fff', borderStyle: 'dashed' }}
|
||||
>
|
||||
<Icon type="plus" /> New Category
|
||||
</Tag>
|
||||
)}
|
||||
</TweenOneGroup>
|
||||
@ -414,24 +467,23 @@ class ManageCategories extends React.Component {
|
||||
<span>
|
||||
<Button
|
||||
onClick={this.handleSave}
|
||||
htmlType="button" type="primary"
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
size="small"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
</span>
|
||||
)}
|
||||
<Button
|
||||
onClick={this.handleCloseButton}
|
||||
size="small">
|
||||
<Button onClick={this.handleCloseButton} size="small">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<Divider dashed="true"/>
|
||||
<div style={{marginTop: 8}}>
|
||||
)}
|
||||
<Divider dashed="true" />
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
@ -442,7 +494,7 @@ class ManageCategories extends React.Component {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{categoriesElements}
|
||||
@ -456,8 +508,11 @@ class ManageCategories extends React.Component {
|
||||
onCancel={this.closeEditModal}
|
||||
onOk={this.editItem}
|
||||
>
|
||||
<Input value={this.state.editingValue} ref={(input) => this.editingInput = input}
|
||||
onChange={this.handleEditInputChange}/>
|
||||
<Input
|
||||
value={this.state.editingValue}
|
||||
ref={input => (this.editingInput = input)}
|
||||
onChange={this.handleEditInputChange}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
Card,
|
||||
Tag,
|
||||
@ -30,15 +30,17 @@ import {
|
||||
Tooltip,
|
||||
Popconfirm,
|
||||
Modal,
|
||||
Row, Col,
|
||||
Typography, Alert
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import {TweenOneGroup} from 'rc-tween-one';
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
Row,
|
||||
Col,
|
||||
Typography,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import { TweenOneGroup } from 'rc-tween-one';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../js/Utils';
|
||||
|
||||
const {Title} = Typography;
|
||||
const { Title } = Typography;
|
||||
|
||||
class ManageTags extends React.Component {
|
||||
state = {
|
||||
@ -53,35 +55,44 @@ class ManageTags extends React.Component {
|
||||
currentlyEditingId: null,
|
||||
editingValue: null,
|
||||
forbiddenErrors: {
|
||||
tags: false
|
||||
}
|
||||
tags: false,
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let tags = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
tags: tags,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load tags.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load tags.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.tags = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -90,62 +101,66 @@ class ManageTags extends React.Component {
|
||||
handleCloseButton = () => {
|
||||
this.setState({
|
||||
tempElements: [],
|
||||
isAddNewVisible: false
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
deleteTag = (id) => {
|
||||
deleteTag = id => {
|
||||
const config = this.props.context;
|
||||
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
|
||||
axios.delete(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/tags/" + id
|
||||
).then(res => {
|
||||
axios
|
||||
.delete(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/tags/' +
|
||||
id,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Tag Removed Successfully!",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Tag Removed Successfully!',
|
||||
});
|
||||
|
||||
const {tags} = this.state;
|
||||
const remainingElements = tags.filter(function (value) {
|
||||
const { tags } = this.state;
|
||||
const remainingElements = tags.filter(function(value) {
|
||||
return value.tagName !== id;
|
||||
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
tags: remainingElements
|
||||
tags: remainingElements,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to delete the tag.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to delete the tag.');
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
renderElement = (tag) => {
|
||||
renderElement = tag => {
|
||||
const tagName = tag.tagName;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
color="#34495e"
|
||||
style={{marginTop: 8}}
|
||||
>
|
||||
<Tag color="#34495e" style={{ marginTop: 8 }}>
|
||||
{tagName}
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="edit">
|
||||
<Icon onClick={() => {
|
||||
this.openEditModal(tagName)
|
||||
}} type="edit"/>
|
||||
<Icon
|
||||
onClick={() => {
|
||||
this.openEditModal(tagName);
|
||||
}}
|
||||
type="edit"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="delete">
|
||||
<Popconfirm
|
||||
title="Are you sure delete this tag?"
|
||||
@ -153,43 +168,41 @@ class ManageTags extends React.Component {
|
||||
if (tag.isTagDeletable) {
|
||||
this.deleteTag(tagName);
|
||||
} else {
|
||||
notification["error"]({
|
||||
notification.error({
|
||||
message: 'Cannot delete "' + tagName + '"',
|
||||
description:
|
||||
"This tag is currently used. Please unassign the tag from apps.",
|
||||
'This tag is currently used. Please unassign the tag from apps.',
|
||||
});
|
||||
}
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Icon type="delete"/>
|
||||
<Icon type="delete" />
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={tag.tagName} style={{display: 'inline-block'}}>
|
||||
<span key={tag.tagName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
renderTempElement = (tag) => {
|
||||
const {tempElements} = this.state;
|
||||
renderTempElement = tag => {
|
||||
const { tempElements } = this.state;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
style={{marginTop: 8}}
|
||||
style={{ marginTop: 8 }}
|
||||
closable
|
||||
onClose={e => {
|
||||
e.preventDefault();
|
||||
const remainingElements = tempElements.filter(function (value) {
|
||||
|
||||
const remainingElements = tempElements.filter(function(value) {
|
||||
return value.tagName !== tag.tagName;
|
||||
|
||||
});
|
||||
this.setState({
|
||||
tempElements: remainingElements
|
||||
tempElements: remainingElements,
|
||||
});
|
||||
}}
|
||||
>
|
||||
@ -197,26 +210,32 @@ class ManageTags extends React.Component {
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={tag.tagName} style={{display: 'inline-block'}}>
|
||||
<span key={tag.tagName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
showInput = () => {
|
||||
this.setState({inputVisible: true}, () => this.input.focus());
|
||||
this.setState({ inputVisible: true }, () => this.input.focus());
|
||||
};
|
||||
|
||||
handleInputChange = e => {
|
||||
this.setState({inputValue: e.target.value});
|
||||
this.setState({ inputValue: e.target.value });
|
||||
};
|
||||
|
||||
handleInputConfirm = () => {
|
||||
const {inputValue, tags} = this.state;
|
||||
let {tempElements} = this.state;
|
||||
const { inputValue, tags } = this.state;
|
||||
let { tempElements } = this.state;
|
||||
if (inputValue) {
|
||||
if ((tags.findIndex(i => i.tagName === inputValue) === -1) && (tempElements.findIndex(i => i.tagName === inputValue) === -1)) {
|
||||
tempElements = [...tempElements, {tagName: inputValue, isTagDeletable: true}];
|
||||
if (
|
||||
tags.findIndex(i => i.tagName === inputValue) === -1 &&
|
||||
tempElements.findIndex(i => i.tagName === inputValue) === -1
|
||||
) {
|
||||
tempElements = [
|
||||
...tempElements,
|
||||
{ tagName: inputValue, isTagDeletable: true },
|
||||
];
|
||||
} else {
|
||||
message.warning('Tag already exists');
|
||||
}
|
||||
@ -231,21 +250,26 @@ class ManageTags extends React.Component {
|
||||
|
||||
handleSave = () => {
|
||||
const config = this.props.context;
|
||||
const {tempElements, tags} = this.state;
|
||||
const { tempElements, tags } = this.state;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const data = tempElements.map(tag => tag.tagName);
|
||||
|
||||
axios.post(window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags',
|
||||
data,
|
||||
).then(res => {
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"New tags were added successfully",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'New tags were added successfully',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
@ -254,18 +278,16 @@ class ManageTags extends React.Component {
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
loading: false,
|
||||
isAddNewVisible: false
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to delete tag.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to delete tag.');
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
saveInputRef = input => (this.input = input);
|
||||
@ -273,77 +295,92 @@ class ManageTags extends React.Component {
|
||||
closeEditModal = e => {
|
||||
this.setState({
|
||||
isEditModalVisible: false,
|
||||
currentlyEditingId: null
|
||||
currentlyEditingId: null,
|
||||
});
|
||||
};
|
||||
|
||||
openEditModal = (id) => {
|
||||
openEditModal = id => {
|
||||
this.setState({
|
||||
isEditModalVisible: true,
|
||||
currentlyEditingId: id,
|
||||
editingValue: id
|
||||
})
|
||||
editingValue: id,
|
||||
});
|
||||
};
|
||||
|
||||
editItem = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
const {editingValue, currentlyEditingId, tags} = this.state;
|
||||
const { editingValue, currentlyEditingId, tags } = this.state;
|
||||
|
||||
this.setState({
|
||||
loading: true,
|
||||
isEditModalVisible: false,
|
||||
});
|
||||
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags/rename?from=" + currentlyEditingId + "&to=" + editingValue,
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags/rename?from=' +
|
||||
currentlyEditingId +
|
||||
'&to=' +
|
||||
editingValue,
|
||||
{},
|
||||
).then(res => {
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Tag was edited successfully",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Tag was edited successfully',
|
||||
});
|
||||
|
||||
tags[tags.findIndex(i => i.tagName === currentlyEditingId)].tagName = editingValue;
|
||||
tags[
|
||||
tags.findIndex(i => i.tagName === currentlyEditingId)
|
||||
].tagName = editingValue;
|
||||
|
||||
this.setState({
|
||||
tags: tags,
|
||||
loading: false,
|
||||
editingValue: null
|
||||
editingValue: null,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to edit tag.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to edit tag.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
editingValue: null
|
||||
editingValue: null,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
handleEditInputChange = (e) => {
|
||||
handleEditInputChange = e => {
|
||||
this.setState({
|
||||
editingValue: e.target.value
|
||||
editingValue: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {tags, inputVisible, inputValue, tempElements, isAddNewVisible, forbiddenErrors} = this.state;
|
||||
const {
|
||||
tags,
|
||||
inputVisible,
|
||||
inputValue,
|
||||
tempElements,
|
||||
isAddNewVisible,
|
||||
forbiddenErrors,
|
||||
} = this.state;
|
||||
const tagsElements = tags.map(this.renderElement);
|
||||
const temporaryElements = tempElements.map(this.renderTempElement);
|
||||
return (
|
||||
<div style={{marginBottom: 16}}>
|
||||
{(forbiddenErrors.tags) && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
{forbiddenErrors.tags && (
|
||||
<Alert
|
||||
message="You don't have permission to view tags."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Card>
|
||||
<Spin tip="Working on it..." spinning={this.state.loading}>
|
||||
@ -352,27 +389,33 @@ class ManageTags extends React.Component {
|
||||
<Title level={4}>Tags</Title>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isAddNewVisible &&
|
||||
<div style={{float: "right"}}>
|
||||
{!isAddNewVisible && (
|
||||
<div style={{ float: 'right' }}>
|
||||
<Button
|
||||
icon="plus"
|
||||
// type="primary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
this.setState(
|
||||
{
|
||||
isAddNewVisible: true,
|
||||
inputVisible: true
|
||||
}, () => this.input.focus())
|
||||
}} htmlType="button">Add
|
||||
inputVisible: true,
|
||||
},
|
||||
() => this.input.focus(),
|
||||
);
|
||||
}}
|
||||
htmlType="button"
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
{isAddNewVisible &&
|
||||
{isAddNewVisible && (
|
||||
<div>
|
||||
<Divider/>
|
||||
<div style={{marginBottom: 16}}>
|
||||
<Divider />
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
@ -383,8 +426,9 @@ class ManageTags extends React.Component {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
|
||||
appear={false}>
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{temporaryElements}
|
||||
|
||||
{inputVisible && (
|
||||
@ -392,7 +436,7 @@ class ManageTags extends React.Component {
|
||||
ref={this.saveInputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
style={{width: 120}}
|
||||
style={{ width: 120 }}
|
||||
value={inputValue}
|
||||
onChange={this.handleInputChange}
|
||||
onBlur={this.handleInputConfirm}
|
||||
@ -400,9 +444,11 @@ class ManageTags extends React.Component {
|
||||
/>
|
||||
)}
|
||||
{!inputVisible && (
|
||||
<Tag onClick={this.showInput}
|
||||
style={{background: '#fff', borderStyle: 'dashed'}}>
|
||||
<Icon type="plus"/> New Tag
|
||||
<Tag
|
||||
onClick={this.showInput}
|
||||
style={{ background: '#fff', borderStyle: 'dashed' }}
|
||||
>
|
||||
<Icon type="plus" /> New Tag
|
||||
</Tag>
|
||||
)}
|
||||
</TweenOneGroup>
|
||||
@ -412,24 +458,24 @@ class ManageTags extends React.Component {
|
||||
<span>
|
||||
<Button
|
||||
onClick={this.handleSave}
|
||||
htmlType="button" type="primary"
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
size="small"
|
||||
disabled={tempElements.length === 0}>
|
||||
disabled={tempElements.length === 0}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Divider type="vertical" />
|
||||
</span>
|
||||
)}
|
||||
< Button
|
||||
onClick={this.handleCloseButton}
|
||||
size="small">
|
||||
<Button onClick={this.handleCloseButton} size="small">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<Divider dashed="true"/>
|
||||
<div style={{marginTop: 8}}>
|
||||
)}
|
||||
<Divider dashed="true" />
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
@ -440,7 +486,7 @@ class ManageTags extends React.Component {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{tagsElements}
|
||||
@ -448,14 +494,17 @@ class ManageTags extends React.Component {
|
||||
</div>
|
||||
</Spin>
|
||||
</Card>
|
||||
< Modal
|
||||
<Modal
|
||||
title="Edit"
|
||||
visible={this.state.isEditModalVisible}
|
||||
onCancel={this.closeEditModal}
|
||||
onOk={this.editItem}
|
||||
>
|
||||
<Input value={this.state.editingValue} ref={(input) => this.editingInput = input}
|
||||
onChange={this.handleEditInputChange}/>
|
||||
<Input
|
||||
value={this.state.editingValue}
|
||||
ref={input => (this.editingInput = input)}
|
||||
onChange={this.handleEditInputChange}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,30 +16,18 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Steps,
|
||||
Row,
|
||||
Col,
|
||||
Form,
|
||||
Result,
|
||||
notification,
|
||||
Spin
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import {withRouter} from 'react-router-dom';
|
||||
import NewAppDetailsForm from "./subForms/NewAppDetailsForm";
|
||||
import NewAppUploadForm from "./subForms/NewAppUploadForm";
|
||||
import {withConfigContext} from "../../context/ConfigContext";
|
||||
import {handleApiError} from "../../js/Utils";
|
||||
|
||||
const {Step} = Steps;
|
||||
import React from 'react';
|
||||
import { Card, Button, Steps, Row, Col, Form, Result, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import NewAppDetailsForm from './subForms/NewAppDetailsForm';
|
||||
import NewAppUploadForm from './subForms/NewAppUploadForm';
|
||||
import { withConfigContext } from '../../context/ConfigContext';
|
||||
import { handleApiError } from '../../js/Utils';
|
||||
|
||||
const { Step } = Steps;
|
||||
|
||||
class AddNewAppFormComponent extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -55,89 +43,98 @@ class AddNewAppFormComponent extends React.Component {
|
||||
isError: false,
|
||||
deviceType: null,
|
||||
supportedOsVersions: [],
|
||||
errorText: "",
|
||||
errorText: '',
|
||||
forbiddenErrors: {
|
||||
supportedOsVersions: false
|
||||
}
|
||||
supportedOsVersions: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
onSuccessApplicationData = (application) => {
|
||||
const {formConfig} = this.props;
|
||||
if (application.hasOwnProperty("deviceType") &&
|
||||
formConfig.installationType !== "WEB_CLIP" &&
|
||||
formConfig.installationType !== "CUSTOM") {
|
||||
onSuccessApplicationData = application => {
|
||||
const { formConfig } = this.props;
|
||||
if (
|
||||
application.hasOwnProperty('deviceType') &&
|
||||
formConfig.installationType !== 'WEB_CLIP' &&
|
||||
formConfig.installationType !== 'CUSTOM'
|
||||
) {
|
||||
this.getSupportedOsVersions(application.deviceType);
|
||||
}
|
||||
this.setState({
|
||||
application,
|
||||
current: 1
|
||||
current: 1,
|
||||
});
|
||||
};
|
||||
|
||||
onSuccessReleaseData = (releaseData) => {
|
||||
onSuccessReleaseData = releaseData => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true,
|
||||
isError: false
|
||||
isError: false,
|
||||
});
|
||||
const {application} = this.state;
|
||||
const {data, release} = releaseData;
|
||||
const {formConfig} = this.props;
|
||||
const {price} = release;
|
||||
const { application } = this.state;
|
||||
const { data, release } = releaseData;
|
||||
const { formConfig } = this.props;
|
||||
const { price } = release;
|
||||
|
||||
application.subMethod = (price === 0) ? "FREE" : "PAID";
|
||||
//add release wrapper
|
||||
application.subMethod = price === 0 ? 'FREE' : 'PAID';
|
||||
// add release wrapper
|
||||
application[formConfig.releaseWrapperName] = [release];
|
||||
|
||||
const json = JSON.stringify(application);
|
||||
const blob = new Blob([json], {
|
||||
type: 'application/json'
|
||||
type: 'application/json',
|
||||
});
|
||||
data.append(formConfig.jsonPayloadName, blob);
|
||||
|
||||
const url = window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications" + formConfig.endpoint;
|
||||
const url =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications' +
|
||||
formConfig.endpoint;
|
||||
|
||||
axios.post(
|
||||
url,
|
||||
data
|
||||
).then(res => {
|
||||
axios
|
||||
.post(url, data)
|
||||
.then(res => {
|
||||
if (res.status === 201) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
current: 2
|
||||
current: 2,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
isError: true,
|
||||
current: 2
|
||||
current: 2,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, error.response.data.data);
|
||||
this.setState({
|
||||
loading: false,
|
||||
isError: true,
|
||||
current: 2,
|
||||
errorText: error.response.data.data
|
||||
errorText: error.response.data.data,
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
onClickBackButton = () => {
|
||||
const current = this.state.current - 1;
|
||||
this.setState({current});
|
||||
this.setState({ current });
|
||||
};
|
||||
|
||||
getSupportedOsVersions = (deviceType) => {
|
||||
getSupportedOsVersions = deviceType => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt +
|
||||
`/admin/device-types/${deviceType}/versions`
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
`/admin/device-types/${deviceType}/versions`,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let supportedOsVersions = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
@ -145,80 +142,105 @@ class AddNewAppFormComponent extends React.Component {
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load supported OS versions.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load supported OS versions.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.supportedOsVersions = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {loading, current, isError, supportedOsVersions, errorText, forbiddenErrors} = this.state;
|
||||
const {formConfig} = this.props;
|
||||
const {
|
||||
loading,
|
||||
current,
|
||||
isError,
|
||||
supportedOsVersions,
|
||||
errorText,
|
||||
forbiddenErrors,
|
||||
} = this.state;
|
||||
const { formConfig } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Spin tip="Uploading..." spinning={loading}>
|
||||
<Row>
|
||||
<Col span={16} offset={4}>
|
||||
<Steps style={{minHeight: 32}} current={current}>
|
||||
<Step key="Application" title="Application"/>
|
||||
<Step key="Release" title="Release"/>
|
||||
<Step key="Result" title="Result"/>
|
||||
<Steps style={{ minHeight: 32 }} current={current}>
|
||||
<Step key="Application" title="Application" />
|
||||
<Step key="Release" title="Release" />
|
||||
<Step key="Result" title="Result" />
|
||||
</Steps>
|
||||
<Card style={{marginTop: 24}}>
|
||||
<div style={{display: (current === 0 ? 'unset' : 'none')}}>
|
||||
<Card style={{ marginTop: 24 }}>
|
||||
<div style={{ display: current === 0 ? 'unset' : 'none' }}>
|
||||
<NewAppDetailsForm
|
||||
formConfig={formConfig}
|
||||
onSuccessApplicationData={this.onSuccessApplicationData}/>
|
||||
onSuccessApplicationData={this.onSuccessApplicationData}
|
||||
/>
|
||||
</div>
|
||||
<div style={{display: (current === 1 ? 'unset' : 'none')}}>
|
||||
<div style={{ display: current === 1 ? 'unset' : 'none' }}>
|
||||
<NewAppUploadForm
|
||||
forbiddenErrors={forbiddenErrors}
|
||||
formConfig={formConfig}
|
||||
supportedOsVersions={supportedOsVersions}
|
||||
onSuccessReleaseData={this.onSuccessReleaseData}
|
||||
onClickBackButton={this.onClickBackButton}/>
|
||||
onClickBackButton={this.onClickBackButton}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{display: (current === 2 ? 'unset' : 'none')}}>
|
||||
|
||||
{!isError && (<Result
|
||||
<div style={{ display: current === 2 ? 'unset' : 'none' }}>
|
||||
{!isError && (
|
||||
<Result
|
||||
status="success"
|
||||
title="Application created successfully!"
|
||||
extra={[
|
||||
<Button type="primary" key="console"
|
||||
onClick={() => this.props.history.push('/publisher/apps')}>
|
||||
<Button
|
||||
type="primary"
|
||||
key="console"
|
||||
onClick={() =>
|
||||
this.props.history.push('/publisher/apps')
|
||||
}
|
||||
>
|
||||
Go to applications
|
||||
</Button>
|
||||
</Button>,
|
||||
]}
|
||||
/>)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isError && (<Result
|
||||
{isError && (
|
||||
<Result
|
||||
status="500"
|
||||
title={errorText}
|
||||
subTitle="Go back to edit the details and submit again."
|
||||
extra={<Button onClick={this.onClickBackButton}>Back</Button>}
|
||||
/>)}
|
||||
extra={
|
||||
<Button onClick={this.onClickBackButton}>Back</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AddNewAppForm = withRouter(Form.create({name: 'add-new-app'})(AddNewAppFormComponent));
|
||||
const AddNewAppForm = withRouter(
|
||||
Form.create({ name: 'add-new-app' })(AddNewAppFormComponent),
|
||||
);
|
||||
export default withConfigContext(AddNewAppForm);
|
||||
|
||||
@ -16,28 +16,27 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Alert, Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Spin, Switch, Upload} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
import React from 'react';
|
||||
import { Alert, Button, Col, Form, Input, Row, Select, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../js/Utils';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: {span: 24},
|
||||
sm: {span: 5},
|
||||
xs: { span: 24 },
|
||||
sm: { span: 5 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: {span: 24},
|
||||
sm: {span: 19},
|
||||
xs: { span: 24 },
|
||||
sm: { span: 19 },
|
||||
},
|
||||
};
|
||||
const {Option} = Select;
|
||||
const {TextArea} = Input;
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
|
||||
class NewAppDetailsForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -51,8 +50,8 @@ class NewAppDetailsForm extends React.Component {
|
||||
categories: false,
|
||||
tags: false,
|
||||
deviceTypes: false,
|
||||
roles: false
|
||||
}
|
||||
roles: false,
|
||||
},
|
||||
};
|
||||
this.lastFetchId = 0;
|
||||
this.fetchRoles = debounce(this.fetchRoles, 800);
|
||||
@ -60,15 +59,20 @@ class NewAppDetailsForm extends React.Component {
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const {formConfig} = this.props;
|
||||
const {specificElements} = formConfig;
|
||||
const { formConfig } = this.props;
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
const {name, description, categories, tags, unrestrictedRoles} = values;
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
categories,
|
||||
tags,
|
||||
unrestrictedRoles,
|
||||
} = values;
|
||||
const unrestrictedRolesData = [];
|
||||
unrestrictedRoles.map(val => {
|
||||
unrestrictedRolesData.push(val.key);
|
||||
@ -81,11 +85,11 @@ class NewAppDetailsForm extends React.Component {
|
||||
unrestrictedRoles: unrestrictedRolesData,
|
||||
};
|
||||
|
||||
if (formConfig.installationType !== "WEB_CLIP") {
|
||||
if (formConfig.installationType !== 'WEB_CLIP') {
|
||||
application.deviceType = values.deviceType;
|
||||
} else {
|
||||
application.type = "WEB_CLIP";
|
||||
application.deviceType = "ALL";
|
||||
application.type = 'WEB_CLIP';
|
||||
application.deviceType = 'ALL';
|
||||
}
|
||||
|
||||
this.props.onSuccessApplicationData(application);
|
||||
@ -101,28 +105,38 @@ class NewAppDetailsForm extends React.Component {
|
||||
|
||||
getCategories = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/categories',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let categories = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
categories: categories,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load categories.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.categories = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -130,9 +144,14 @@ class NewAppDetailsForm extends React.Component {
|
||||
|
||||
getTags = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let tags = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
@ -140,18 +159,23 @@ class NewAppDetailsForm extends React.Component {
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load tags.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load tags.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.tags = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -159,19 +183,24 @@ class NewAppDetailsForm extends React.Component {
|
||||
|
||||
getDeviceTypes = () => {
|
||||
const config = this.props.context;
|
||||
const {formConfig} = this.props;
|
||||
const {installationType} = formConfig;
|
||||
const { formConfig } = this.props;
|
||||
const { installationType } = formConfig;
|
||||
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/device-types"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
'/device-types',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const allDeviceTypes = JSON.parse(res.data.data);
|
||||
const mobileDeviceTypes = config.deviceTypes.mobileTypes;
|
||||
const allowedDeviceTypes = [];
|
||||
|
||||
// exclude mobile device types if installation type is custom
|
||||
if (installationType === "CUSTOM") {
|
||||
if (installationType === 'CUSTOM') {
|
||||
allDeviceTypes.forEach(deviceType => {
|
||||
if (!mobileDeviceTypes.includes(deviceType.name)) {
|
||||
allowedDeviceTypes.push(deviceType);
|
||||
@ -190,18 +219,23 @@ class NewAppDetailsForm extends React.Component {
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load device types.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load device types.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.deviceTypes = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -211,11 +245,17 @@ class NewAppDetailsForm extends React.Component {
|
||||
const config = this.props.context;
|
||||
this.lastFetchId += 1;
|
||||
const fetchId = this.lastFetchId;
|
||||
this.setState({data: [], fetching: true});
|
||||
this.setState({ data: [], fetching: true });
|
||||
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/roles?filter=" + value,
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
'/roles?filter=' +
|
||||
value,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
if (fetchId !== this.lastFetchId) {
|
||||
// for fetch callback order
|
||||
@ -229,22 +269,26 @@ class NewAppDetailsForm extends React.Component {
|
||||
|
||||
this.setState({
|
||||
unrestrictedRoles: data,
|
||||
fetching: false
|
||||
fetching: false,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load roles.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load roles.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.roles = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
fetching: false
|
||||
})
|
||||
fetching: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
fetching: false
|
||||
fetching: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -259,176 +303,182 @@ class NewAppDetailsForm extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {formConfig} = this.props;
|
||||
const {categories, tags, deviceTypes, fetching, roleSearchValue, unrestrictedRoles, forbiddenErrors} = this.state;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const { formConfig } = this.props;
|
||||
const {
|
||||
categories,
|
||||
tags,
|
||||
deviceTypes,
|
||||
fetching,
|
||||
unrestrictedRoles,
|
||||
forbiddenErrors,
|
||||
} = this.state;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col md={5}>
|
||||
|
||||
</Col>
|
||||
<Col md={5}></Col>
|
||||
<Col md={14}>
|
||||
<Form
|
||||
labelAlign="right"
|
||||
layout="horizontal"
|
||||
onSubmit={this.handleSubmit}>
|
||||
{formConfig.installationType !== "WEB_CLIP" && (
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{formConfig.installationType !== 'WEB_CLIP' && (
|
||||
<div>
|
||||
{(forbiddenErrors.deviceTypes) && (
|
||||
{forbiddenErrors.deviceTypes && (
|
||||
<Alert
|
||||
message="You don't have permission to view device types."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Device Type">
|
||||
{getFieldDecorator('deviceType', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select device type'
|
||||
}
|
||||
message: 'Please select device type',
|
||||
},
|
||||
],
|
||||
}
|
||||
)(
|
||||
})(
|
||||
<Select
|
||||
style={{width: '100%'}}
|
||||
placeholder="select device type">
|
||||
{
|
||||
deviceTypes.map(deviceType => {
|
||||
style={{ width: '100%' }}
|
||||
placeholder="select device type"
|
||||
>
|
||||
{deviceTypes.map(deviceType => {
|
||||
return (
|
||||
<Option
|
||||
key={deviceType.name}>
|
||||
<Option key={deviceType.name}>
|
||||
{deviceType.name}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
);
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/*app name*/}
|
||||
{/* app name*/}
|
||||
<Form.Item {...formItemLayout} label="App Name">
|
||||
{getFieldDecorator('name', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input a name'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="ex: Lorem App"/>
|
||||
)}
|
||||
message: 'Please input a name',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="ex: Lorem App" />)}
|
||||
</Form.Item>
|
||||
|
||||
{/*description*/}
|
||||
{/* description*/}
|
||||
<Form.Item {...formItemLayout} label="Description">
|
||||
{getFieldDecorator('description', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a description'
|
||||
}],
|
||||
message: 'Please enter a description',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea placeholder="Enter the description..." rows={7}/>
|
||||
<TextArea placeholder="Enter the description..." rows={7} />,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{/*Unrestricted Roles*/}
|
||||
{(forbiddenErrors.roles) && (
|
||||
{/* Unrestricted Roles*/}
|
||||
{forbiddenErrors.roles && (
|
||||
<Alert
|
||||
message="You don't have permission to view roles."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Visible Roles">
|
||||
{getFieldDecorator('unrestrictedRoles', {
|
||||
rules: [],
|
||||
initialValue: []
|
||||
initialValue: [],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
labelInValue
|
||||
// value={roleSearchValue}
|
||||
placeholder="Search roles"
|
||||
notFoundContent={fetching ? <Spin size="small"/> : null}
|
||||
notFoundContent={fetching ? <Spin size="small" /> : null}
|
||||
filterOption={false}
|
||||
onSearch={this.fetchRoles}
|
||||
onChange={this.handleRoleSearch}
|
||||
style={{width: '100%'}}>
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{unrestrictedRoles.map(d => (
|
||||
<Option key={d.value}>{d.text}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{(forbiddenErrors.categories) && (
|
||||
{forbiddenErrors.categories && (
|
||||
<Alert
|
||||
message="You don't have permission to view categories."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Categories">
|
||||
{getFieldDecorator('categories', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select categories'
|
||||
}],
|
||||
message: 'Please select categories',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Category"
|
||||
onChange={this.handleCategoryChange}>
|
||||
{
|
||||
categories.map(category => {
|
||||
onChange={this.handleCategoryChange}
|
||||
>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<Option
|
||||
key={category.categoryName}>
|
||||
<Option key={category.categoryName}>
|
||||
{category.categoryName}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
);
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{(forbiddenErrors.tags) && (
|
||||
{forbiddenErrors.tags && (
|
||||
<Alert
|
||||
message="You don't have permission to view tags."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Tags">
|
||||
{getFieldDecorator('tags', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select tags'
|
||||
}],
|
||||
message: 'Please select tags',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{width: '100%'}}
|
||||
placeholder="Tags">
|
||||
{
|
||||
tags.map(tag => {
|
||||
return (
|
||||
<Option
|
||||
key={tag.tagName}>
|
||||
{tag.tagName}
|
||||
</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Tags"
|
||||
>
|
||||
{tags.map(tag => {
|
||||
return <Option key={tag.tagName}>{tag.tagName}</Option>;
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{float: "right"}}>
|
||||
<Form.Item style={{ float: 'right' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Next
|
||||
</Button>
|
||||
@ -439,7 +489,8 @@ class NewAppDetailsForm extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withConfigContext(Form.create({name: 'app-details-form'})(NewAppDetailsForm));
|
||||
export default withConfigContext(
|
||||
Form.create({ name: 'app-details-form' })(NewAppDetailsForm),
|
||||
);
|
||||
|
||||
@ -16,24 +16,36 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, Col, Form, Icon, Input, Row, Select, Switch, Upload, Typography, Modal, Alert, Tooltip} from "antd";
|
||||
import "@babel/polyfill";
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Icon,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Upload,
|
||||
Typography,
|
||||
Modal,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import '@babel/polyfill';
|
||||
|
||||
const {Text} = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: {span: 24},
|
||||
sm: {span: 8},
|
||||
xs: { span: 24 },
|
||||
sm: { span: 8 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: {span: 24},
|
||||
sm: {span: 16},
|
||||
xs: { span: 24 },
|
||||
sm: { span: 16 },
|
||||
},
|
||||
};
|
||||
const {Option} = Select;
|
||||
const {TextArea} = Input;
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
const InputGroup = Input.Group;
|
||||
|
||||
function getBase64(file) {
|
||||
@ -46,7 +58,6 @@ function getBase64(file) {
|
||||
}
|
||||
|
||||
class NewAppUploadForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -63,7 +74,7 @@ class NewAppUploadForm extends React.Component {
|
||||
screenshotHelperText: '',
|
||||
osVersionsHelperText: '',
|
||||
osVersionsValidateStatus: 'validating',
|
||||
metaData: []
|
||||
metaData: [],
|
||||
};
|
||||
this.lowerOsVersion = null;
|
||||
this.upperOsVersion = null;
|
||||
@ -78,46 +89,57 @@ class NewAppUploadForm extends React.Component {
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const {formConfig} = this.props;
|
||||
const {specificElements} = formConfig;
|
||||
const { formConfig } = this.props;
|
||||
const { specificElements } = formConfig;
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
const {price, isSharedWithAllTenants, binaryFile, icon, screenshots, releaseDescription, releaseType} = values;
|
||||
const {
|
||||
binaryFile,
|
||||
icon,
|
||||
screenshots,
|
||||
releaseDescription,
|
||||
releaseType,
|
||||
} = values;
|
||||
|
||||
//add release data
|
||||
// add release data
|
||||
const release = {
|
||||
description: releaseDescription,
|
||||
price: 0,
|
||||
isSharedWithAllTenants: false,
|
||||
metaData: JSON.stringify(this.state.metaData),
|
||||
releaseType: releaseType
|
||||
releaseType: releaseType,
|
||||
};
|
||||
|
||||
if (specificElements.hasOwnProperty("version")) {
|
||||
if (specificElements.hasOwnProperty('version')) {
|
||||
release.version = values.version;
|
||||
}
|
||||
if (specificElements.hasOwnProperty("url")) {
|
||||
if (specificElements.hasOwnProperty('url')) {
|
||||
release.url = values.url;
|
||||
}
|
||||
if (specificElements.hasOwnProperty("packageName")) {
|
||||
if (specificElements.hasOwnProperty('packageName')) {
|
||||
release.packageName = values.packageName;
|
||||
}
|
||||
|
||||
const data = new FormData();
|
||||
let isFormValid = true; // flag to check if this form is valid
|
||||
|
||||
if (formConfig.installationType !== "WEB_CLIP" && formConfig.installationType !== "CUSTOM") {
|
||||
if (
|
||||
formConfig.installationType !== 'WEB_CLIP' &&
|
||||
formConfig.installationType !== 'CUSTOM'
|
||||
) {
|
||||
if (this.lowerOsVersion == null || this.upperOsVersion == null) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
osVersionsHelperText: 'Please select supported OS versions',
|
||||
osVersionsValidateStatus: 'error',
|
||||
});
|
||||
} else if (parseFloat(this.lowerOsVersion) >= parseFloat(this.upperOsVersion)) {
|
||||
} else if (
|
||||
parseFloat(this.lowerOsVersion) >= parseFloat(this.upperOsVersion)
|
||||
) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
osVersionsHelperText: 'Please select valid range',
|
||||
@ -128,28 +150,31 @@ class NewAppUploadForm extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty("binaryFile") && this.state.binaryFiles.length !== 1) {
|
||||
if (
|
||||
specificElements.hasOwnProperty('binaryFile') &&
|
||||
this.state.binaryFiles.length !== 1
|
||||
) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
binaryFileHelperText: 'Please select the application'
|
||||
binaryFileHelperText: 'Please select the application',
|
||||
});
|
||||
}
|
||||
if (this.state.icons.length !== 1) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
iconHelperText: 'Please select an icon'
|
||||
iconHelperText: 'Please select an icon',
|
||||
});
|
||||
}
|
||||
if (this.state.screenshots.length !== 3) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
screenshotHelperText: 'Please select 3 screenshots'
|
||||
screenshotHelperText: 'Please select 3 screenshots',
|
||||
});
|
||||
}
|
||||
if (this.state.screenshots.length !== 3) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
screenshotHelperText: 'Please select 3 screenshots'
|
||||
screenshotHelperText: 'Please select 3 screenshots',
|
||||
});
|
||||
}
|
||||
if (isFormValid) {
|
||||
@ -157,46 +182,46 @@ class NewAppUploadForm extends React.Component {
|
||||
data.append('screenshot1', screenshots[0].originFileObj);
|
||||
data.append('screenshot2', screenshots[1].originFileObj);
|
||||
data.append('screenshot3', screenshots[2].originFileObj);
|
||||
if (specificElements.hasOwnProperty("binaryFile")) {
|
||||
if (specificElements.hasOwnProperty('binaryFile')) {
|
||||
data.append('binaryFile', binaryFile[0].originFileObj);
|
||||
}
|
||||
this.props.onSuccessReleaseData({data, release});
|
||||
this.props.onSuccessReleaseData({ data, release });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleIconChange = ({fileList}) => {
|
||||
handleIconChange = ({ fileList }) => {
|
||||
if (fileList.length === 1) {
|
||||
this.setState({
|
||||
iconHelperText: ''
|
||||
iconHelperText: '',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
icons: fileList
|
||||
icons: fileList,
|
||||
});
|
||||
};
|
||||
handleBinaryFileChange = ({fileList}) => {
|
||||
handleBinaryFileChange = ({ fileList }) => {
|
||||
if (fileList.length === 1) {
|
||||
this.setState({
|
||||
binaryFileHelperText: ''
|
||||
binaryFileHelperText: '',
|
||||
});
|
||||
}
|
||||
this.setState({binaryFiles: fileList});
|
||||
this.setState({ binaryFiles: fileList });
|
||||
};
|
||||
|
||||
handleScreenshotChange = ({fileList}) => {
|
||||
handleScreenshotChange = ({ fileList }) => {
|
||||
if (fileList.length === 3) {
|
||||
this.setState({
|
||||
screenshotHelperText: ''
|
||||
screenshotHelperText: '',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
screenshots: fileList
|
||||
screenshots: fileList,
|
||||
});
|
||||
};
|
||||
|
||||
handlePreviewCancel = () => this.setState({previewVisible: false});
|
||||
handlePreviewCancel = () => this.setState({ previewVisible: false });
|
||||
handlePreview = async file => {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = await getBase64(file.originFileObj);
|
||||
@ -210,33 +235,35 @@ class NewAppUploadForm extends React.Component {
|
||||
|
||||
addNewMetaData = () => {
|
||||
this.setState({
|
||||
metaData: this.state.metaData.concat({'key': '', 'value': ''})
|
||||
})
|
||||
};
|
||||
|
||||
handleLowerOsVersionChange = (lowerOsVersion) => {
|
||||
this.lowerOsVersion = lowerOsVersion;
|
||||
this.setState({
|
||||
osVersionsValidateStatus: 'validating',
|
||||
osVersionsHelperText: ''
|
||||
metaData: this.state.metaData.concat({ key: '', value: '' }),
|
||||
});
|
||||
};
|
||||
|
||||
handleUpperOsVersionChange = (upperOsVersion) => {
|
||||
if (upperOsVersion === "all") {
|
||||
this.upperOsVersion = this.props.supportedOsVersions[this.props.supportedOsVersions.length - 1]["versionName"];
|
||||
handleLowerOsVersionChange = lowerOsVersion => {
|
||||
this.lowerOsVersion = lowerOsVersion;
|
||||
this.setState({
|
||||
osVersionsValidateStatus: 'validating',
|
||||
osVersionsHelperText: '',
|
||||
});
|
||||
};
|
||||
|
||||
handleUpperOsVersionChange = upperOsVersion => {
|
||||
if (upperOsVersion === 'all') {
|
||||
this.upperOsVersion = this.props.supportedOsVersions[
|
||||
this.props.supportedOsVersions.length - 1
|
||||
].versionName;
|
||||
} else {
|
||||
this.upperOsVersion = upperOsVersion;
|
||||
}
|
||||
this.setState({
|
||||
osVersionsValidateStatus: 'validating',
|
||||
osVersionsHelperText: ''
|
||||
osVersionsHelperText: '',
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {formConfig, supportedOsVersions} = this.props;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const { formConfig, supportedOsVersions } = this.props;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const {
|
||||
icons,
|
||||
screenshots,
|
||||
@ -248,11 +275,11 @@ class NewAppUploadForm extends React.Component {
|
||||
screenshotHelperText,
|
||||
metaData,
|
||||
osVersionsHelperText,
|
||||
osVersionsValidateStatus
|
||||
osVersionsValidateStatus,
|
||||
} = this.state;
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<Icon type="plus"/>
|
||||
<Icon type="plus" />
|
||||
<div className="ant-upload-text">Select</div>
|
||||
</div>
|
||||
);
|
||||
@ -260,32 +287,34 @@ class NewAppUploadForm extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col md={5}>
|
||||
|
||||
</Col>
|
||||
<Col md={5}></Col>
|
||||
<Col md={14}>
|
||||
<Form
|
||||
labelAlign="right"
|
||||
layout="horizontal"
|
||||
onSubmit={this.handleSubmit}>
|
||||
{formConfig.specificElements.hasOwnProperty("binaryFile") && (
|
||||
<Form.Item {...formItemLayout}
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{formConfig.specificElements.hasOwnProperty('binaryFile') && (
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Application"
|
||||
validateStatus="error"
|
||||
help={binaryFileHelperText}>
|
||||
help={binaryFileHelperText}
|
||||
>
|
||||
{getFieldDecorator('binaryFile', {
|
||||
valuePropName: 'binaryFile',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select application'
|
||||
message: 'Please select application',
|
||||
})(
|
||||
<Upload
|
||||
name="binaryFile"
|
||||
onChange={this.handleBinaryFileChange}
|
||||
beforeUpload={() => false}>
|
||||
beforeUpload={() => false}
|
||||
>
|
||||
{binaryFiles.length !== 1 && (
|
||||
<Button>
|
||||
<Icon type="upload"/> Click to upload
|
||||
<Icon type="upload" /> Click to upload
|
||||
</Button>
|
||||
)}
|
||||
</Upload>,
|
||||
@ -293,68 +322,74 @@ class NewAppUploadForm extends React.Component {
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item {...formItemLayout}
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Icon"
|
||||
validateStatus="error"
|
||||
help={iconHelperText}
|
||||
style={{
|
||||
marginBottom: 0
|
||||
}}>
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
{getFieldDecorator('icon', {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon'
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="logo"
|
||||
listType="picture-card"
|
||||
onChange={this.handleIconChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}>
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{icons.length === 1 ? null : uploadButton}
|
||||
</Upload>
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Row style={{
|
||||
marginBottom: 24
|
||||
}}>
|
||||
<Col xs={24} sm={8}>
|
||||
</Col>
|
||||
<Row
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
<Col xs={24} sm={8}></Col>
|
||||
<Col xs={24} sm={16}>
|
||||
<Text type="secondary">
|
||||
Recommended : 240px x 240px
|
||||
</Text>
|
||||
<Text type="secondary">Recommended : 240px x 240px</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item {...formItemLayout}
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Screenshots"
|
||||
validateStatus="error"
|
||||
help={screenshotHelperText}
|
||||
style={{
|
||||
marginBottom: 0
|
||||
}}>
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
{getFieldDecorator('screenshots', {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon'
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="screenshots"
|
||||
listType="picture-card"
|
||||
onChange={this.handleScreenshotChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}>
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{screenshots.length >= 3 ? null : uploadButton}
|
||||
</Upload>
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Row style={{
|
||||
marginBottom: 24
|
||||
}}>
|
||||
<Col xs={24} sm={8}>
|
||||
</Col>
|
||||
<Row
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
<Col xs={24} sm={8}></Col>
|
||||
<Col xs={24} sm={16}>
|
||||
<Text type="secondary">
|
||||
It is mandatory to upload three screenshots and the
|
||||
@ -362,86 +397,96 @@ class NewAppUploadForm extends React.Component {
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
{formConfig.specificElements.hasOwnProperty("packageName") && (
|
||||
{formConfig.specificElements.hasOwnProperty('packageName') && (
|
||||
<Form.Item {...formItemLayout} label="Package Name">
|
||||
{getFieldDecorator('packageName', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the package name'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="Package Name"/>
|
||||
)}
|
||||
message: 'Please input the package name',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Package Name" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{formConfig.specificElements.hasOwnProperty("url") && (
|
||||
{formConfig.specificElements.hasOwnProperty('url') && (
|
||||
<Form.Item {...formItemLayout} label="URL">
|
||||
{getFieldDecorator('url', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the url'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="url"/>
|
||||
)}
|
||||
message: 'Please input the url',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="url" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{formConfig.specificElements.hasOwnProperty("version") && (
|
||||
{formConfig.specificElements.hasOwnProperty('version') && (
|
||||
<Form.Item {...formItemLayout} label="Version">
|
||||
{getFieldDecorator('version', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the version'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="Version"/>
|
||||
)}
|
||||
message: 'Please input the version',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Version" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item {...formItemLayout} label="Release Type">
|
||||
{getFieldDecorator('releaseType', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the Release Type'
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="Release Type"/>
|
||||
)}
|
||||
message: 'Please input the Release Type',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Release Type" />)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...formItemLayout} label="Description">
|
||||
{getFieldDecorator('releaseDescription', {
|
||||
rules: [{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a description for release'
|
||||
}],
|
||||
message: 'Please enter a description for release',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea placeholder="Enter a description for release" rows={5}/>
|
||||
<TextArea
|
||||
placeholder="Enter a description for release"
|
||||
rows={5}
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{(formConfig.installationType !== "WEB_CLIP" && formConfig.installationType !== "CUSTOM") && (
|
||||
{formConfig.installationType !== 'WEB_CLIP' &&
|
||||
formConfig.installationType !== 'CUSTOM' && (
|
||||
<div>
|
||||
{(this.props.forbiddenErrors.supportedOsVersions) && (
|
||||
{this.props.forbiddenErrors.supportedOsVersions && (
|
||||
<Alert
|
||||
message="You don't have permission to view supported OS versions."
|
||||
type="warning"
|
||||
banner
|
||||
closable/>
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Supported OS Versions"
|
||||
validateStatus={osVersionsValidateStatus}
|
||||
help={osVersionsHelperText}>
|
||||
help={osVersionsHelperText}
|
||||
>
|
||||
{getFieldDecorator('supportedOS', {
|
||||
rules: [{
|
||||
required: true
|
||||
}],
|
||||
initialValue: false
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
initialValue: false,
|
||||
})(
|
||||
<div>
|
||||
<InputGroup>
|
||||
@ -449,11 +494,14 @@ class NewAppUploadForm extends React.Component {
|
||||
<Col span={11}>
|
||||
<Select
|
||||
placeholder="Lower version"
|
||||
style={{width: "100%"}}
|
||||
onChange={this.handleLowerOsVersionChange}>
|
||||
style={{ width: '100%' }}
|
||||
onChange={this.handleLowerOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option key={version.versionName}
|
||||
value={version.versionName}>
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
@ -463,19 +511,22 @@ class NewAppUploadForm extends React.Component {
|
||||
<p> - </p>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Select style={{width: "100%"}}
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Upper version"
|
||||
defaultActiveFirstOption={true}
|
||||
onChange={this.handleUpperOsVersionChange}>
|
||||
{(supportedOsVersions.length > 0) && (
|
||||
<Option key="all"
|
||||
value="all">
|
||||
onChange={this.handleUpperOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.length > 0 && (
|
||||
<Option key="all" value="all">
|
||||
All
|
||||
</Option>
|
||||
)}
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option key={version.versionName}
|
||||
value={version.versionName}>
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
@ -483,7 +534,7 @@ class NewAppUploadForm extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
@ -491,8 +542,7 @@ class NewAppUploadForm extends React.Component {
|
||||
<Form.Item {...formItemLayout} label="Meta Data">
|
||||
{getFieldDecorator('meta', {})(
|
||||
<div>
|
||||
{
|
||||
metaData.map((data, index) => {
|
||||
{metaData.map((data, index) => {
|
||||
return (
|
||||
<InputGroup key={index}>
|
||||
<Row gutter={8}>
|
||||
@ -500,66 +550,83 @@ class NewAppUploadForm extends React.Component {
|
||||
<Input
|
||||
placeholder="key"
|
||||
value={data.key}
|
||||
onChange={(e) => {
|
||||
metaData[index]['key'] = e.currentTarget.value;
|
||||
onChange={e => {
|
||||
metaData[index].key = e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData
|
||||
})
|
||||
}}/>
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Input
|
||||
placeholder="value"
|
||||
value={data.value}
|
||||
onChange={(e) => {
|
||||
onChange={e => {
|
||||
metaData[index].value = e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData
|
||||
metaData,
|
||||
});
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<Button type="dashed"
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="circle"
|
||||
icon="minus"
|
||||
onClick={() => {
|
||||
metaData.splice(index, 1);
|
||||
this.setState({
|
||||
metaData
|
||||
metaData,
|
||||
});
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
<Button type="dashed" icon="plus" onClick={this.addNewMetaData}>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
type="dashed"
|
||||
icon="plus"
|
||||
onClick={this.addNewMetaData}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{float: "right", marginLeft: 8}}>
|
||||
<Form.Item style={{ float: 'right', marginLeft: 8 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item style={{float: "right"}}>
|
||||
<Button htmlType="button" onClick={this.props.onClickBackButton}>
|
||||
<Form.Item style={{ float: 'right' }}>
|
||||
<Button
|
||||
htmlType="button"
|
||||
onClick={this.props.onClickBackButton}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
<Modal visible={previewVisible} footer={null} onCancel={this.handlePreviewCancel}>
|
||||
<img alt="Preview Image" style={{width: '100%'}} src={previewImage}/>
|
||||
<Modal
|
||||
visible={previewVisible}
|
||||
footer={null}
|
||||
onCancel={this.handlePreviewCancel}
|
||||
>
|
||||
<img
|
||||
alt="Preview Image"
|
||||
style={{ width: '100%' }}
|
||||
src={previewImage}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default (Form.create({name: 'app-upload-form'})(NewAppUploadForm));
|
||||
export default Form.create({ name: 'app-upload-form' })(NewAppUploadForm);
|
||||
|
||||
@ -16,24 +16,23 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Form, notification, Spin, Card, Row, Col} from "antd";
|
||||
import axios from "axios";
|
||||
import {withRouter} from 'react-router-dom'
|
||||
import {withConfigContext} from "../../context/ConfigContext";
|
||||
import {handleApiError} from "../../js/Utils";
|
||||
import NewAppUploadForm from "../new-app/subForms/NewAppUploadForm";
|
||||
import React from 'react';
|
||||
import { Form, notification, Spin, Card, Row, Col } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { withConfigContext } from '../../context/ConfigContext';
|
||||
import { handleApiError } from '../../js/Utils';
|
||||
import NewAppUploadForm from '../new-app/subForms/NewAppUploadForm';
|
||||
|
||||
const formConfig = {
|
||||
specificElements: {
|
||||
binaryFile: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewReleaseFormComponent extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -43,8 +42,8 @@ class AddNewReleaseFormComponent extends React.Component {
|
||||
release: null,
|
||||
deviceType: null,
|
||||
forbiddenErrors: {
|
||||
supportedOsVersions: false
|
||||
}
|
||||
supportedOsVersions: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -52,12 +51,16 @@ class AddNewReleaseFormComponent extends React.Component {
|
||||
this.getSupportedOsVersions(this.props.deviceType);
|
||||
}
|
||||
|
||||
getSupportedOsVersions = (deviceType) => {
|
||||
getSupportedOsVersions = deviceType => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt +
|
||||
`/admin/device-types/${deviceType}/versions`
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
`/admin/device-types/${deviceType}/versions`,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let supportedOsVersions = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
@ -65,68 +68,79 @@ class AddNewReleaseFormComponent extends React.Component {
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load supported OS versions.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load supported OS versions.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.supportedOsVersions = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onSuccessReleaseData = (releaseData) => {
|
||||
onSuccessReleaseData = releaseData => {
|
||||
const config = this.props.context;
|
||||
const {appId, deviceType} = this.props;
|
||||
const { appId, deviceType } = this.props;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
const {data, release} = releaseData;
|
||||
const { data, release } = releaseData;
|
||||
|
||||
const json = JSON.stringify(release);
|
||||
const blob = new Blob([json], {
|
||||
type: 'application/json'
|
||||
type: 'application/json',
|
||||
});
|
||||
data.append("applicationRelease", blob);
|
||||
data.append('applicationRelease', blob);
|
||||
|
||||
const url = window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher +
|
||||
"/applications/" + deviceType + "/ent-app/" + appId;
|
||||
axios.post(
|
||||
url,
|
||||
data
|
||||
).then(res => {
|
||||
const url =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/' +
|
||||
deviceType +
|
||||
'/ent-app/' +
|
||||
appId;
|
||||
axios
|
||||
.post(url, data)
|
||||
.then(res => {
|
||||
if (res.status === 201) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"New release was added successfully",
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'New release was added successfully',
|
||||
});
|
||||
const uuid = res.data.data.uuid;
|
||||
this.props.history.push('/publisher/apps/releases/' + uuid);
|
||||
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Sorry, we were unable to complete your request.");
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Sorry, we were unable to complete your request.',
|
||||
);
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
onClickBackButton = () => {
|
||||
@ -134,7 +148,7 @@ class AddNewReleaseFormComponent extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {loading, supportedOsVersions, forbiddenErrors} = this.state;
|
||||
const { loading, supportedOsVersions, forbiddenErrors } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Spin tip="Uploading..." spinning={loading}>
|
||||
@ -157,5 +171,7 @@ class AddNewReleaseFormComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const AddReleaseForm = withRouter(Form.create({name: 'add-new-release'})(AddNewReleaseFormComponent));
|
||||
const AddReleaseForm = withRouter(
|
||||
Form.create({ name: 'add-new-release' })(AddNewReleaseFormComponent),
|
||||
);
|
||||
export default withConfigContext(AddReleaseForm);
|
||||
|
||||
@ -16,19 +16,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
const ConfigContext = React.createContext();
|
||||
|
||||
export const withConfigContext = Component => {
|
||||
// eslint-disable-next-line react/display-name
|
||||
return props => (
|
||||
<ConfigContext.Consumer>
|
||||
{context => {
|
||||
return <Component {...props} context={context}/>;
|
||||
return <Component {...props} context={context} />;
|
||||
}}
|
||||
</ConfigContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigContext;
|
||||
|
||||
|
||||
@ -19,27 +19,26 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import App from "./App";
|
||||
import Login from "./pages/Login";
|
||||
import Dashboard from "./pages/dashboard/Dashboard";
|
||||
import Apps from "./pages/dashboard/apps/Apps";
|
||||
import Release from "./pages/dashboard/apps/release/Release";
|
||||
import AddNewEnterpriseApp from "./pages/dashboard/add-new-app/AddNewEnterpriseApp";
|
||||
import Mange from "./pages/dashboard/manage/Manage";
|
||||
import App from './App';
|
||||
import Login from './pages/Login';
|
||||
import Dashboard from './pages/dashboard/Dashboard';
|
||||
import Apps from './pages/dashboard/apps/Apps';
|
||||
import Release from './pages/dashboard/apps/release/Release';
|
||||
import AddNewEnterpriseApp from './pages/dashboard/add-new-app/AddNewEnterpriseApp';
|
||||
import Mange from './pages/dashboard/manage/Manage';
|
||||
import './index.css';
|
||||
import AddNewPublicApp from "./pages/dashboard/add-new-app/AddNewPublicApp";
|
||||
import AddNewWebClip from "./pages/dashboard/add-new-app/AddNewWebClip";
|
||||
import AddNewRelease from "./pages/dashboard/add-new-release/AddNewRelease";
|
||||
import AddNewCustomApp from "./pages/dashboard/add-new-app/AddNewCustomApp";
|
||||
import ManageAndroidEnterprise from "./pages/dashboard/manage/android-enterprise/ManageAndroidEnterprise";
|
||||
import Page from "./pages/dashboard/manage/android-enterprise/page/Page";
|
||||
|
||||
import AddNewPublicApp from './pages/dashboard/add-new-app/AddNewPublicApp';
|
||||
import AddNewWebClip from './pages/dashboard/add-new-app/AddNewWebClip';
|
||||
import AddNewRelease from './pages/dashboard/add-new-release/AddNewRelease';
|
||||
import AddNewCustomApp from './pages/dashboard/add-new-app/AddNewCustomApp';
|
||||
import ManageAndroidEnterprise from './pages/dashboard/manage/android-enterprise/ManageAndroidEnterprise';
|
||||
import Page from './pages/dashboard/manage/android-enterprise/page/Page';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/publisher/login',
|
||||
exact: true,
|
||||
component: Login
|
||||
component: Login,
|
||||
},
|
||||
{
|
||||
path: '/publisher/',
|
||||
@ -49,61 +48,58 @@ const routes = [
|
||||
{
|
||||
path: '/publisher/apps',
|
||||
component: Apps,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/apps/releases/:uuid',
|
||||
exact: true,
|
||||
component: Release
|
||||
component: Release,
|
||||
},
|
||||
{
|
||||
path: '/publisher/apps/:deviceType/:appId/add-release',
|
||||
component: AddNewRelease,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/enterprise',
|
||||
component: AddNewEnterpriseApp,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/public',
|
||||
component: AddNewPublicApp,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/web-clip',
|
||||
component: AddNewWebClip,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/custom-app',
|
||||
component: AddNewCustomApp,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/manage',
|
||||
component: Mange,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/manage/android-enterprise',
|
||||
component: ManageAndroidEnterprise,
|
||||
exact: true
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/manage/android-enterprise/pages/:pageName/:pageId',
|
||||
component: Page,
|
||||
exact: true
|
||||
}
|
||||
]
|
||||
}
|
||||
exact: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<App routes={routes}/>,
|
||||
document.getElementById('root'));
|
||||
ReactDOM.render(<App routes={routes} />, document.getElementById('root'));
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
|
||||
@ -16,16 +16,27 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {notification} from "antd";
|
||||
import { notification } from 'antd';
|
||||
|
||||
export const handleApiError = (error, message, isForbiddenMessageSilent = false) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
export const handleApiError = (
|
||||
error,
|
||||
message,
|
||||
isForbiddenMessageSilent = false,
|
||||
) => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 401) {
|
||||
const redirectUrl = encodeURI(window.location.href);
|
||||
window.location.href = window.location.origin + `/publisher/login?redirect=${redirectUrl}`;
|
||||
window.location.href =
|
||||
window.location.origin + `/publisher/login?redirect=${redirectUrl}`;
|
||||
// silence 403 forbidden message
|
||||
} else if (!(isForbiddenMessageSilent && error.hasOwnProperty("response") && error.response.status === 403)) {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
} else if (
|
||||
!(
|
||||
isForbiddenMessageSilent &&
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 403
|
||||
)
|
||||
) {
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 10,
|
||||
description: message,
|
||||
});
|
||||
|
||||
@ -16,126 +16,135 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox, message, notification} from 'antd';
|
||||
import React from 'react';
|
||||
import {
|
||||
Typography,
|
||||
Row,
|
||||
Col,
|
||||
Form,
|
||||
Icon,
|
||||
Input,
|
||||
Button,
|
||||
message,
|
||||
notification,
|
||||
} from 'antd';
|
||||
import './Login.css';
|
||||
import axios from 'axios';
|
||||
import "./Login.css";
|
||||
import {withConfigContext} from "../context/ConfigContext";
|
||||
import {handleApiError} from "../js/Utils";
|
||||
import './Login.css';
|
||||
import { withConfigContext } from '../context/ConfigContext';
|
||||
|
||||
const {Title} = Typography;
|
||||
const {Text} = Typography;
|
||||
const { Title } = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
class Login extends React.Component {
|
||||
render() {
|
||||
const config = this.props.context;
|
||||
return (
|
||||
<div className="login">
|
||||
<div className="background">
|
||||
</div>
|
||||
<div className="background"></div>
|
||||
<div className="content">
|
||||
<Row>
|
||||
<Col xs={3} sm={3} md={10}>
|
||||
</Col>
|
||||
<Col xs={3} sm={3} md={10}></Col>
|
||||
<Col xs={18} sm={18} md={4}>
|
||||
<Row style={{marginBottom: 20}}>
|
||||
<Col style={{textAlign: "center"}}>
|
||||
<img style={
|
||||
{
|
||||
<Row style={{ marginBottom: 20 }}>
|
||||
<Col style={{ textAlign: 'center' }}>
|
||||
<img
|
||||
style={{
|
||||
marginTop: 36,
|
||||
height: 60
|
||||
}
|
||||
}
|
||||
src={config.theme.logo}/>
|
||||
height: 60,
|
||||
}}
|
||||
src={config.theme.logo}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Title level={2}>Login</Title>
|
||||
<WrappedNormalLoginForm/>
|
||||
<WrappedNormalLoginForm />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={4} offset={10}>
|
||||
|
||||
</Col>
|
||||
<Col span={4} offset={10}></Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NormalLoginForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inValid: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit = (e) => {
|
||||
handleSubmit = e => {
|
||||
const config = this.props.context;
|
||||
const thisForm = this;
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((err, values) => {
|
||||
thisForm.setState({
|
||||
inValid: false
|
||||
inValid: false,
|
||||
});
|
||||
if (!err) {
|
||||
thisForm.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
const parameters = {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
platform: "publisher"
|
||||
platform: 'publisher',
|
||||
};
|
||||
|
||||
const request = Object.keys(parameters).map(key => key + '=' + parameters[key]).join('&');
|
||||
const request = Object.keys(parameters)
|
||||
.map(key => key + '=' + parameters[key])
|
||||
.join('&');
|
||||
|
||||
axios.post(window.location.origin+ config.serverConfig.loginUri, request
|
||||
).then(res=>{
|
||||
axios
|
||||
.post(window.location.origin + config.serverConfig.loginUri, request)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let redirectUrl = window.location.origin+"/publisher";
|
||||
let redirectUrl = window.location.origin + '/publisher';
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if(searchParams.has("redirect")){
|
||||
redirectUrl = searchParams.get("redirect");
|
||||
if (searchParams.has('redirect')) {
|
||||
redirectUrl = searchParams.get('redirect');
|
||||
}
|
||||
window.location = redirectUrl;
|
||||
}
|
||||
}).catch(function (error) {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
thisForm.setState({
|
||||
loading: false,
|
||||
inValid: true
|
||||
inValid: true,
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 10,
|
||||
description: message,
|
||||
});
|
||||
thisForm.setState({
|
||||
loading: false,
|
||||
inValid: false
|
||||
inValid: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
let errorMsg = "";
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
let errorMsg = '';
|
||||
if (this.state.inValid) {
|
||||
errorMsg = <Text type="danger">Invalid Login Details</Text>;
|
||||
}
|
||||
let loading = "";
|
||||
let loading = '';
|
||||
if (this.state.loading) {
|
||||
loading = <Text type="secondary">Loading..</Text>;
|
||||
}
|
||||
@ -143,25 +152,37 @@ class NormalLoginForm extends React.Component {
|
||||
<Form onSubmit={this.handleSubmit} className="login-form">
|
||||
<Form.Item>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{required: true, message: 'Please enter your username'}],
|
||||
rules: [{ required: true, message: 'Please enter your username' }],
|
||||
})(
|
||||
<Input style={{height: 32}} prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
|
||||
placeholder="Username"/>
|
||||
<Input
|
||||
style={{ height: 32 }}
|
||||
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
|
||||
placeholder="Username"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{required: true, message: 'Please enter your password'}],
|
||||
rules: [{ required: true, message: 'Please enter your password' }],
|
||||
})(
|
||||
<Input style={{height: 32}}
|
||||
prefix={<Icon type="lock" style={{color: 'rgba(0,0,0,.25)'}}/>} type="password"
|
||||
placeholder="Password"/>
|
||||
<Input
|
||||
style={{ height: 32 }}
|
||||
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{loading}
|
||||
{errorMsg}
|
||||
<Form.Item>
|
||||
<Button loading={this.state.loading} block type="primary" htmlType="submit" className="login-form-button">
|
||||
<Button
|
||||
loading={this.state.loading}
|
||||
block
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
className="login-form-button"
|
||||
>
|
||||
Log in
|
||||
</Button>
|
||||
</Form.Item>
|
||||
@ -170,6 +191,8 @@ class NormalLoginForm extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const WrappedNormalLoginForm = Form.create({name: 'normal_login'})(withConfigContext(NormalLoginForm));
|
||||
const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(
|
||||
withConfigContext(NormalLoginForm),
|
||||
);
|
||||
|
||||
export default withConfigContext(Login);
|
||||
|
||||
@ -16,17 +16,17 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Layout, Menu, Icon, Drawer, Button} from 'antd';
|
||||
import {Switch, Link} from "react-router-dom";
|
||||
import RouteWithSubRoutes from "../../components/RouteWithSubRoutes"
|
||||
import {Redirect} from 'react-router'
|
||||
import "./Dashboard.css";
|
||||
import {withConfigContext} from "../../context/ConfigContext";
|
||||
import Logout from "./logout/Logout";
|
||||
import React from 'react';
|
||||
import { Layout, Menu, Icon, Drawer, Button } from 'antd';
|
||||
import { Switch, Link } from 'react-router-dom';
|
||||
import RouteWithSubRoutes from '../../components/RouteWithSubRoutes';
|
||||
import { Redirect } from 'react-router';
|
||||
import './Dashboard.css';
|
||||
import { withConfigContext } from '../../context/ConfigContext';
|
||||
import Logout from './logout/Logout';
|
||||
|
||||
const {Header, Content, Footer} = Layout;
|
||||
const {SubMenu} = Menu;
|
||||
const { Header, Content, Footer } = Layout;
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
constructor(props) {
|
||||
@ -34,7 +34,7 @@ class Dashboard extends React.Component {
|
||||
this.state = {
|
||||
routes: props.routes,
|
||||
visible: false,
|
||||
collapsed: false
|
||||
collapsed: false,
|
||||
};
|
||||
this.config = this.props.context;
|
||||
this.Logo = this.config.theme.logo;
|
||||
@ -44,7 +44,7 @@ class Dashboard extends React.Component {
|
||||
showMobileNavigationBar = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
collapsed: !this.state.collapsed
|
||||
collapsed: !this.state.collapsed,
|
||||
});
|
||||
};
|
||||
|
||||
@ -58,9 +58,17 @@ class Dashboard extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<Layout>
|
||||
<Header style={{paddingLeft: 0, paddingRight: 0, backgroundColor: "white"}}>
|
||||
<Header
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
>
|
||||
<div className="logo-image">
|
||||
<Link to="/publisher/apps"><img alt="logo" src={this.Logo}/></Link>
|
||||
<Link to="/publisher/apps">
|
||||
<img alt="logo" src={this.Logo} />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="web-layout">
|
||||
@ -68,21 +76,25 @@ class Dashboard extends React.Component {
|
||||
theme="light"
|
||||
mode="horizontal"
|
||||
defaultSelectedKeys={['1']}
|
||||
style={{lineHeight: '64px'}}>
|
||||
<Menu.Item key="1"><Link to="/publisher/apps"><Icon
|
||||
type="appstore"/>Apps</Link></Menu.Item>
|
||||
style={{ lineHeight: '64px' }}
|
||||
>
|
||||
<Menu.Item key="1">
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="appstore" />
|
||||
Apps
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<Icon type="plus"/>
|
||||
<Icon type="plus" />
|
||||
Add New App
|
||||
</span>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="add-new-public-app">
|
||||
<Link to="/publisher/add-new-app/public">
|
||||
Public App
|
||||
</Link>
|
||||
<Link to="/publisher/add-new-app/public">Public App</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="add-new-enterprise-app">
|
||||
<Link to="/publisher/add-new-app/enterprise">
|
||||
@ -90,9 +102,7 @@ class Dashboard extends React.Component {
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="add-new-web-clip">
|
||||
<Link to="/publisher/add-new-app/web-clip">
|
||||
Web Clip
|
||||
</Link>
|
||||
<Link to="/publisher/add-new-app/web-clip">Web Clip</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="add-new-custom-app">
|
||||
<Link to="/publisher/add-new-app/custom-app">
|
||||
@ -104,28 +114,36 @@ class Dashboard extends React.Component {
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<Icon type="control"/>Manage
|
||||
</span>}>
|
||||
<Icon type="control" />
|
||||
Manage
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="manage">
|
||||
<Link to="/publisher/manage">
|
||||
<Icon type="setting"/> General
|
||||
<Icon type="setting" /> General
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{this.config.androidEnterpriseToken != null && (
|
||||
<Menu.Item key="manage-android-enterprise">
|
||||
<Link to="/publisher/manage/android-enterprise">
|
||||
<Icon type="android" theme="filled"/> Android Enterprise
|
||||
<Icon type="android" theme="filled" /> Android
|
||||
Enterprise
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</SubMenu>
|
||||
|
||||
<SubMenu className="profile"
|
||||
<SubMenu
|
||||
className="profile"
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<Icon type="user"/>{this.config.user}
|
||||
</span>}>
|
||||
<Logout/>
|
||||
<Icon type="user" />
|
||||
{this.config.user}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Logout />
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</div>
|
||||
@ -137,17 +155,23 @@ class Dashboard extends React.Component {
|
||||
<Button type="link" onClick={this.showMobileNavigationBar}>
|
||||
<Icon
|
||||
type={this.state.collapsed ? 'menu-fold' : 'menu-unfold'}
|
||||
className="bar-icon"/>
|
||||
className="bar-icon"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</Layout>
|
||||
<Drawer
|
||||
title={
|
||||
<Link to="/publisher/apps" onClick={this.onCloseMobileNavigationBar}>
|
||||
<img alt="logo"
|
||||
<Link
|
||||
to="/publisher/apps"
|
||||
onClick={this.onCloseMobileNavigationBar}
|
||||
>
|
||||
<img
|
||||
alt="logo"
|
||||
src={this.Logo}
|
||||
style={{marginLeft: 30}}
|
||||
width={"60%"}/>
|
||||
style={{ marginLeft: 30 }}
|
||||
width={'60%'}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
placement="left"
|
||||
@ -155,29 +179,36 @@ class Dashboard extends React.Component {
|
||||
onClose={this.onCloseMobileNavigationBar}
|
||||
visible={this.state.visible}
|
||||
getContainer={false}
|
||||
style={{position: 'absolute'}}>
|
||||
style={{ position: 'absolute' }}
|
||||
>
|
||||
<Menu
|
||||
theme="light"
|
||||
mode="inline"
|
||||
defaultSelectedKeys={['1']}
|
||||
style={{lineHeight: '64px', width: 231}}
|
||||
onClick={this.onCloseMobileNavigationBar}>
|
||||
style={{ lineHeight: '64px', width: 231 }}
|
||||
onClick={this.onCloseMobileNavigationBar}
|
||||
>
|
||||
<Menu.Item key="1">
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="appstore"/>Apps
|
||||
<Icon type="appstore" />
|
||||
Apps
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<Icon type="plus"/>Add New App
|
||||
<Icon type="plus" />
|
||||
Add New App
|
||||
</span>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="setting:1">
|
||||
<Link to="/publisher/add-new-app/public">Public APP</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="setting:2">
|
||||
<Link to="/publisher/add-new-app/enterprise">Enterprise APP</Link>
|
||||
<Link to="/publisher/add-new-app/enterprise">
|
||||
Enterprise APP
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="setting:3">
|
||||
<Link to="/publisher/add-new-app/web-clip">Web Clip</Link>
|
||||
@ -188,7 +219,8 @@ class Dashboard extends React.Component {
|
||||
</SubMenu>
|
||||
<Menu.Item key="2">
|
||||
<Link to="/publisher/manage">
|
||||
<Icon type="control"/>Manage
|
||||
<Icon type="control" />
|
||||
Manage
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
@ -197,29 +229,30 @@ class Dashboard extends React.Component {
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
defaultSelectedKeys={['1']}
|
||||
style={{lineHeight: '63px', position: 'fixed', marginLeft: '80%'}}>
|
||||
style={{ lineHeight: '63px', position: 'fixed', marginLeft: '80%' }}
|
||||
>
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<Icon type="user"/>
|
||||
</span>}>
|
||||
<Logout/>
|
||||
<Icon type="user" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Logout />
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</Layout>
|
||||
|
||||
<Layout className="dashboard-body">
|
||||
<Content style={{marginTop: 2}}>
|
||||
<Content style={{ marginTop: 2 }}>
|
||||
<Switch>
|
||||
<Redirect exact from="/publisher" to="/publisher/apps"/>
|
||||
{this.state.routes.map((route) => (
|
||||
<Redirect exact from="/publisher" to="/publisher/apps" />
|
||||
{this.state.routes.map(route => (
|
||||
<RouteWithSubRoutes key={route.path} {...route} />
|
||||
))}
|
||||
</Switch>
|
||||
</Content>
|
||||
<Footer style={{textAlign: 'center'}}>
|
||||
{this.footerText}
|
||||
</Footer>
|
||||
<Footer style={{ textAlign: 'center' }}>{this.footerText}</Footer>
|
||||
</Layout>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,67 +16,63 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb,
|
||||
Icon
|
||||
} from "antd";
|
||||
import AddNewAppForm from "../../../components/new-app/AddNewAppForm";
|
||||
import {Link} from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
|
||||
import AddNewAppForm from '../../../components/new-app/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const {Paragraph} = Typography;
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: "CUSTOM",
|
||||
endpoint: "/custom-app",
|
||||
jsonPayloadName: "application",
|
||||
releaseWrapperName: "customAppReleaseWrappers",
|
||||
installationType: 'CUSTOM',
|
||||
endpoint: '/custom-app',
|
||||
jsonPayloadName: 'application',
|
||||
releaseWrapperName: 'customAppReleaseWrappers',
|
||||
specificElements: {
|
||||
binaryFile: {
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
packageName: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
packageName : {
|
||||
required: true
|
||||
},
|
||||
version : {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AddNewCustomApp extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: []
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom:16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Custom App</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Add New Custom App</h3>
|
||||
<Paragraph>Submit and share your own application to the corporate app store.</Paragraph>
|
||||
<Paragraph>
|
||||
Submit and share your own application to the corporate app store.
|
||||
</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<AddNewAppForm formConfig={formConfig}/>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<AddNewAppForm formConfig={formConfig} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,61 +16,57 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb,
|
||||
Icon
|
||||
} from "antd";
|
||||
import AddNewAppForm from "../../../components/new-app/AddNewAppForm";
|
||||
import {Link} from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
|
||||
import AddNewAppForm from '../../../components/new-app/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const {Paragraph} = Typography;
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: "ENTERPRISE",
|
||||
endpoint: "/ent-app",
|
||||
jsonPayloadName: "application",
|
||||
releaseWrapperName: "entAppReleaseWrappers",
|
||||
installationType: 'ENTERPRISE',
|
||||
endpoint: '/ent-app',
|
||||
jsonPayloadName: 'application',
|
||||
releaseWrapperName: 'entAppReleaseWrappers',
|
||||
specificElements: {
|
||||
binaryFile: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewEnterpriseApp extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: []
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom:16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Enterprise App</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Add New Enterprise App</h3>
|
||||
<Paragraph>Submit and share your own application to the corporate app store.</Paragraph>
|
||||
<Paragraph>
|
||||
Submit and share your own application to the corporate app store.
|
||||
</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<AddNewAppForm formConfig={formConfig}/>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<AddNewAppForm formConfig={formConfig} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,40 +16,34 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Icon,
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb
|
||||
} from "antd";
|
||||
import AddNewAppForm from "../../../components/new-app/AddNewAppForm";
|
||||
import {Link} from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { Icon, PageHeader, Typography, Breadcrumb } from 'antd';
|
||||
import AddNewAppForm from '../../../components/new-app/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const {Paragraph, Title} = Typography;
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: "PUBLIC",
|
||||
endpoint: "/public-app",
|
||||
jsonPayloadName:"public-app",
|
||||
releaseWrapperName: "publicAppReleaseWrappers",
|
||||
installationType: 'PUBLIC',
|
||||
endpoint: '/public-app',
|
||||
jsonPayloadName: 'public-app',
|
||||
releaseWrapperName: 'publicAppReleaseWrappers',
|
||||
specificElements: {
|
||||
packageName : {
|
||||
required: true
|
||||
packageName: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
version : {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AddNewEnterpriseApp extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: []
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -57,29 +51,30 @@ class AddNewEnterpriseApp extends React.Component {
|
||||
// this.getCategories();
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom:16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Public App</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Add New Public App</h3>
|
||||
<Paragraph>Share a public application in google play or apple store to your corporate app store.
|
||||
<Paragraph>
|
||||
Share a public application in google play or apple store to your
|
||||
corporate app store.
|
||||
</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<AddNewAppForm formConfig={formConfig}/>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<AddNewAppForm formConfig={formConfig} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,51 +16,46 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Icon,
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb
|
||||
} from "antd";
|
||||
import AddNewAppForm from "../../../components/new-app/AddNewAppForm";
|
||||
import {Link} from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { Icon, PageHeader, Typography, Breadcrumb } from 'antd';
|
||||
import AddNewAppForm from '../../../components/new-app/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const {Paragraph, Title}= Typography;
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: "WEB_CLIP",
|
||||
endpoint: "/web-app",
|
||||
jsonPayloadName:"webapp",
|
||||
releaseWrapperName: "webAppReleaseWrappers",
|
||||
installationType: 'WEB_CLIP',
|
||||
endpoint: '/web-app',
|
||||
jsonPayloadName: 'webapp',
|
||||
releaseWrapperName: 'webAppReleaseWrappers',
|
||||
specificElements: {
|
||||
url : {
|
||||
required: true
|
||||
url: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
version : {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AddNewEnterpriseApp extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: []
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom:16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Web Clip</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
@ -69,12 +64,10 @@ class AddNewEnterpriseApp extends React.Component {
|
||||
<Paragraph>Share a Web Clip to your corporate app store.</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<AddNewAppForm formConfig={formConfig}/>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<AddNewAppForm formConfig={formConfig} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,36 +16,32 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Icon,
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb
|
||||
} from "antd";
|
||||
import AddNewReleaseForm from "../../../components/new-release/AddReleaseForm";
|
||||
import {Link} from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { Icon, PageHeader, Typography, Breadcrumb } from 'antd';
|
||||
import AddNewReleaseForm from '../../../components/new-release/AddReleaseForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Paragraph = Typography;
|
||||
|
||||
class AddNewRelease extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: []
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {appId, deviceType} = this.props.match.params;
|
||||
const { appId, deviceType } = this.props.match.params;
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom: 16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Release</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
@ -54,12 +50,10 @@ class AddNewRelease extends React.Component {
|
||||
<Paragraph>Add new release for the application</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<AddNewReleaseForm deviceType={deviceType} appId={appId} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,26 +16,23 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import ListApps from "../../../components/apps/list-apps/ListApps";
|
||||
import React from 'react';
|
||||
import ListApps from '../../../components/apps/list-apps/ListApps';
|
||||
|
||||
class Apps extends React.Component {
|
||||
routes;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.routes = props.routes;
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 780}}>
|
||||
<ListApps/>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 780 }}>
|
||||
<ListApps />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,17 +16,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import '../../../../App.css';
|
||||
import {Typography, Row, Col, message, Card, notification, Skeleton} from "antd";
|
||||
import { Typography, Row, Col, Card, Skeleton } from 'antd';
|
||||
import axios from 'axios';
|
||||
import ReleaseView from "../../../../components/apps/release/ReleaseView";
|
||||
import LifeCycle from "../../../../components/apps/release/lifeCycle/LifeCycle";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
import NewAppUploadForm from "../../../../components/new-app/subForms/NewAppUploadForm";
|
||||
import ReleaseView from '../../../../components/apps/release/ReleaseView';
|
||||
import LifeCycle from '../../../../components/apps/release/lifeCycle/LifeCycle';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import { handleApiError } from '../../../../js/Utils';
|
||||
|
||||
const {Title} = Typography;
|
||||
const { Title } = Typography;
|
||||
|
||||
class Release extends React.Component {
|
||||
routes;
|
||||
@ -44,132 +43,171 @@ class Release extends React.Component {
|
||||
supportedOsVersions: [],
|
||||
forbiddenErrors: {
|
||||
supportedOsVersions: false,
|
||||
lifeCycle: false
|
||||
}
|
||||
lifeCycle: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {uuid} = this.props.match.params;
|
||||
const { uuid } = this.props.match.params;
|
||||
this.fetchData(uuid);
|
||||
this.getLifecycle();
|
||||
|
||||
}
|
||||
|
||||
changeCurrentLifecycleStatus = (status) => {
|
||||
changeCurrentLifecycleStatus = status => {
|
||||
this.setState({
|
||||
currentLifecycleStatus: status
|
||||
currentLifecycleStatus: status,
|
||||
});
|
||||
};
|
||||
|
||||
updateRelease = (release) => {
|
||||
updateRelease = release => {
|
||||
this.setState({
|
||||
release
|
||||
release,
|
||||
});
|
||||
};
|
||||
|
||||
fetchData = (uuid) => {
|
||||
fetchData = uuid => {
|
||||
const config = this.props.context;
|
||||
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/release/" + uuid,
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/release/' +
|
||||
uuid,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const app = res.data.data;
|
||||
const release = (app !== null) ? app.applicationReleases[0] : null;
|
||||
const currentLifecycleStatus = (release !== null) ? release.currentStatus : null;
|
||||
const release = app !== null ? app.applicationReleases[0] : null;
|
||||
const currentLifecycleStatus =
|
||||
release !== null ? release.currentStatus : null;
|
||||
this.setState({
|
||||
app: app,
|
||||
release: release,
|
||||
currentLifecycleStatus: currentLifecycleStatus,
|
||||
loading: false,
|
||||
uuid: uuid
|
||||
uuid: uuid,
|
||||
});
|
||||
if (config.deviceTypes.mobileTypes.includes(app.deviceType)) {
|
||||
this.getSupportedOsVersions(app.deviceType);
|
||||
}
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load the release.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load the release.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
getLifecycle = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/lifecycle-config"
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/lifecycle-config',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const lifecycle = res.data.data;
|
||||
this.setState({
|
||||
lifecycle: lifecycle
|
||||
})
|
||||
lifecycle: lifecycle,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load lifecycle configuration.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load lifecycle configuration.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.lifeCycle = true;
|
||||
this.setState({
|
||||
forbiddenErrors
|
||||
})
|
||||
forbiddenErrors,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getSupportedOsVersions = (deviceType) => {
|
||||
getSupportedOsVersions = deviceType => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt +
|
||||
`/admin/device-types/${deviceType}/versions`
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
`/admin/device-types/${deviceType}/versions`,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let supportedOsVersions = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
supportedOsVersions
|
||||
supportedOsVersions,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to load supported OS versions.", true);
|
||||
if (error.hasOwnProperty("response") && error.response.status === 403) {
|
||||
const {forbiddenErrors} = this.state;
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load supported OS versions.',
|
||||
true,
|
||||
);
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
const { forbiddenErrors } = this.state;
|
||||
forbiddenErrors.supportedOsVersions = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false
|
||||
})
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {app, release, currentLifecycleStatus, lifecycle, loading, forbiddenErrors} = this.state;
|
||||
const {
|
||||
app,
|
||||
release,
|
||||
currentLifecycleStatus,
|
||||
lifecycle,
|
||||
loading,
|
||||
forbiddenErrors,
|
||||
} = this.state;
|
||||
|
||||
if (release == null && loading === false) {
|
||||
return (
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 780}}>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 780 }}>
|
||||
<Title level={3}>No Apps Found</Title>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
//todo remove uppercase
|
||||
// todo remove uppercase
|
||||
return (
|
||||
<div>
|
||||
<div className="main-container">
|
||||
<Row style={{padding: 10}}>
|
||||
<Col lg={16} md={24} style={{padding: 3}}>
|
||||
<Row style={{ padding: 10 }}>
|
||||
<Col lg={16} md={24} style={{ padding: 3 }}>
|
||||
<Card>
|
||||
<Skeleton loading={loading} avatar={{size: 'large'}} active paragraph={{rows: 18}}>
|
||||
{(release !== null) && (
|
||||
<Skeleton
|
||||
loading={loading}
|
||||
avatar={{ size: 'large' }}
|
||||
active
|
||||
paragraph={{ rows: 18 }}
|
||||
>
|
||||
{release !== null && (
|
||||
<ReleaseView
|
||||
forbiddenErrors={forbiddenErrors}
|
||||
app={app}
|
||||
@ -178,29 +216,30 @@ class Release extends React.Component {
|
||||
lifecycle={lifecycle}
|
||||
updateRelease={this.updateRelease}
|
||||
supportedOsVersions={[...this.state.supportedOsVersions]}
|
||||
/>)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Skeleton>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg={8} md={24} style={{padding: 3}}>
|
||||
<Col lg={8} md={24} style={{ padding: 3 }}>
|
||||
<Card lg={8} md={24}>
|
||||
<Skeleton loading={loading} active paragraph={{rows: 8}}>
|
||||
{(release !== null) && (
|
||||
<Skeleton loading={loading} active paragraph={{ rows: 8 }}>
|
||||
{release !== null && (
|
||||
<LifeCycle
|
||||
uuid={release.uuid}
|
||||
currentStatus={release.currentStatus.toUpperCase()}
|
||||
changeCurrentLifecycleStatus={this.changeCurrentLifecycleStatus}
|
||||
lifecycle={lifecycle}
|
||||
/>)
|
||||
changeCurrentLifecycleStatus={
|
||||
this.changeCurrentLifecycleStatus
|
||||
}
|
||||
lifecycle={lifecycle}
|
||||
/>
|
||||
)}
|
||||
</Skeleton>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,52 +16,51 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {notification, Menu, Icon} from 'antd';
|
||||
import React from 'react';
|
||||
import { notification, Menu, Icon } from 'antd';
|
||||
import axios from 'axios';
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import { withConfigContext } from '../../../context/ConfigContext';
|
||||
|
||||
/*
|
||||
This class for call the logout api by sending request
|
||||
*/
|
||||
class Logout extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inValid: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
/*
|
||||
This function call the logout api when the request is success
|
||||
*/
|
||||
handleSubmit = () => {
|
||||
|
||||
const thisForm = this;
|
||||
const config = this.props.context;
|
||||
|
||||
thisForm.setState({
|
||||
inValid: false
|
||||
inValid: false,
|
||||
});
|
||||
|
||||
axios.post(window.location.origin + config.serverConfig.logoutUri
|
||||
).then(res => {
|
||||
//if the api call status is correct then user will logout and then it goes to login page
|
||||
axios
|
||||
.post(window.location.origin + config.serverConfig.logoutUri)
|
||||
.then(res => {
|
||||
// if the api call status is correct then user will logout and then it goes to login page
|
||||
if (res.status === 200) {
|
||||
window.location = window.location.origin + "/publisher/login";
|
||||
window.location = window.location.origin + '/publisher/login';
|
||||
}
|
||||
}).catch(function (error) {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 400) {
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 400) {
|
||||
thisForm.setState({
|
||||
inValid: true
|
||||
inValid: true,
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to logout.",
|
||||
description: 'Error occurred while trying to logout.',
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -70,7 +69,10 @@ class Logout extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Item key="1" onClick={this.handleSubmit}><Icon type="logout"/>Logout</Menu.Item>
|
||||
<Menu.Item key="1" onClick={this.handleSubmit}>
|
||||
<Icon type="logout" />
|
||||
Logout
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
@ -16,13 +16,13 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {PageHeader, Typography, Breadcrumb, Row, Col, Icon} from "antd";
|
||||
import ManageCategories from "../../../components/manage/categories/ManageCategories";
|
||||
import ManageTags from "../../../components/manage/categories/ManageTags";
|
||||
import {Link} from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { PageHeader, Typography, Breadcrumb, Row, Col, Icon } from 'antd';
|
||||
import ManageCategories from '../../../components/manage/categories/ManageCategories';
|
||||
import ManageTags from '../../../components/manage/categories/ManageTags';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const {Paragraph} = Typography;
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
class Manage extends React.Component {
|
||||
routes;
|
||||
@ -30,40 +30,39 @@ class Manage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.routes = props.routes;
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom: 16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
Manage
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Manage</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>General</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Manage General Settings</h3>
|
||||
<Paragraph>Maintain and manage categories and tags here..</Paragraph>
|
||||
<Paragraph>
|
||||
Maintain and manage categories and tags here..
|
||||
</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 780}}>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 780 }}>
|
||||
<Row gutter={16}>
|
||||
<Col sm={24} md={12}>
|
||||
<ManageCategories/>
|
||||
<ManageCategories />
|
||||
</Col>
|
||||
<Col sm={24} md={12}>
|
||||
<ManageTags/>
|
||||
<ManageTags />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,15 +16,13 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {PageHeader, Typography, Breadcrumb, Divider, Button, Icon} from "antd";
|
||||
import {Link} from "react-router-dom";
|
||||
import SyncAndroidApps from "../../../../components/manage/android-enterprise/SyncAndroidApps";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import GooglePlayIframe from "../../../../components/manage/android-enterprise/GooglePlayIframe";
|
||||
import Pages from "../../../../components/manage/android-enterprise/Pages/Pages";
|
||||
|
||||
const {Paragraph} = Typography;
|
||||
import React from 'react';
|
||||
import { PageHeader, Breadcrumb, Divider, Icon } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import SyncAndroidApps from '../../../../components/manage/android-enterprise/SyncAndroidApps';
|
||||
import { withConfigContext } from '../../../../context/ConfigContext';
|
||||
import GooglePlayIframe from '../../../../components/manage/android-enterprise/GooglePlayIframe';
|
||||
import Pages from '../../../../components/manage/android-enterprise/Pages/Pages';
|
||||
|
||||
class ManageAndroidEnterprise extends React.Component {
|
||||
routes;
|
||||
@ -38,29 +36,28 @@ class ManageAndroidEnterprise extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom: 16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
Manage
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Manage</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Android Enterprise</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Manage Android Enterprise</h3>
|
||||
{/*<Paragraph>Lorem ipsum</Paragraph>*/}
|
||||
{/* <Paragraph>Lorem ipsum</Paragraph>*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<SyncAndroidApps/>
|
||||
<GooglePlayIframe/>
|
||||
<Divider/>
|
||||
<Pages/>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<SyncAndroidApps />
|
||||
<GooglePlayIframe />
|
||||
<Divider />
|
||||
<Pages />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
PageHeader,
|
||||
Typography,
|
||||
@ -28,26 +28,24 @@ import {
|
||||
notification,
|
||||
message,
|
||||
Spin,
|
||||
Select,
|
||||
Tag,
|
||||
Divider
|
||||
} from "antd";
|
||||
import {Link, withRouter} from "react-router-dom";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import axios from "axios";
|
||||
import Cluster from "../../../../../components/manage/android-enterprise/Pages/Cluster/Cluster";
|
||||
import EditLinks from "../../../../../components/manage/android-enterprise/Pages/EditLinks/EditLinks";
|
||||
import {handleApiError} from "../../../../../js/Utils";
|
||||
Divider,
|
||||
} from 'antd';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { withConfigContext } from '../../../../../context/ConfigContext';
|
||||
import axios from 'axios';
|
||||
import Cluster from '../../../../../components/manage/android-enterprise/Pages/Cluster/Cluster';
|
||||
import EditLinks from '../../../../../components/manage/android-enterprise/Pages/EditLinks/EditLinks';
|
||||
import { handleApiError } from '../../../../../js/Utils';
|
||||
|
||||
const {Option} = Select;
|
||||
const {Title, Text} = Typography;
|
||||
const { Title } = Typography;
|
||||
|
||||
class Page extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const {pageName, pageId} = this.props.match.params;
|
||||
const { pageName, pageId } = this.props.match.params;
|
||||
this.pageId = pageId;
|
||||
this.routes = props.routes;
|
||||
this.config = this.props.context;
|
||||
@ -59,7 +57,7 @@ class Page extends React.Component {
|
||||
loading: false,
|
||||
applications: [],
|
||||
isAddNewClusterVisible: false,
|
||||
links: []
|
||||
links: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -69,7 +67,7 @@ class Page extends React.Component {
|
||||
this.fetchPages();
|
||||
}
|
||||
|
||||
removeLoadedCluster = (clusterId) => {
|
||||
removeLoadedCluster = clusterId => {
|
||||
const clusters = [...this.state.clusters];
|
||||
let index = -1;
|
||||
for (let i = 0; i < clusters.length; i++) {
|
||||
@ -80,39 +78,47 @@ class Page extends React.Component {
|
||||
}
|
||||
clusters.splice(index, 1);
|
||||
this.setState({
|
||||
clusters
|
||||
clusters,
|
||||
});
|
||||
};
|
||||
|
||||
updatePageName = pageName => {
|
||||
const config = this.props.context;
|
||||
if (pageName !== this.state.pageName && pageName !== "") {
|
||||
if (pageName !== this.state.pageName && pageName !== '') {
|
||||
const data = {
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
pageName: pageName,
|
||||
pageId: this.pageId
|
||||
pageId: this.pageId,
|
||||
};
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
data
|
||||
).then(res => {
|
||||
axios
|
||||
.put(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/page',
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Page name updated successfully!'
|
||||
description: 'Page name updated successfully!',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
pageName: res.data.data.pageName,
|
||||
});
|
||||
|
||||
this.props.history.push(`/publisher/manage/android-enterprise/pages/${pageName}/${this.pageId}`);
|
||||
|
||||
this.props.history.push(
|
||||
`/publisher/manage/android-enterprise/pages/${pageName}/${this.pageId}`,
|
||||
);
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to save the page name.");
|
||||
this.setState({loading: false});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to save the page name.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -122,7 +128,10 @@ class Page extends React.Component {
|
||||
|
||||
if (swapIndex !== -1 && index < clusters.length) {
|
||||
// swap elements
|
||||
[clusters[index], clusters[swapIndex]] = [clusters[swapIndex], clusters[index]];
|
||||
[clusters[index], clusters[swapIndex]] = [
|
||||
clusters[swapIndex],
|
||||
clusters[index],
|
||||
];
|
||||
|
||||
this.setState({
|
||||
clusters,
|
||||
@ -132,106 +141,118 @@ class Page extends React.Component {
|
||||
|
||||
fetchPages = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
'/device-mgt/android/v1.0/enterprise/store-layout/page',
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
this.pages = res.data.data.page;
|
||||
|
||||
let links = [];
|
||||
|
||||
this.pages.forEach((page) => {
|
||||
this.pageNames[page.id.toString()] = page.name[0]["text"];
|
||||
if (page.id === this.pageId && page.hasOwnProperty("link")) {
|
||||
links = page["link"];
|
||||
this.pages.forEach(page => {
|
||||
this.pageNames[page.id.toString()] = page.name[0].text;
|
||||
if (page.id === this.pageId && page.hasOwnProperty('link')) {
|
||||
links = page.link;
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
links
|
||||
links,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load pages.",
|
||||
description: 'Error occurred while trying to load pages.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
fetchClusters = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
`/device-mgt/android/v1.0/enterprise/store-layout/page/${this.pageId}/clusters`
|
||||
).then(res => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
`/device-mgt/android/v1.0/enterprise/store-layout/page/${this.pageId}/clusters`,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let clusters = JSON.parse(res.data.data);
|
||||
|
||||
// sort according to the orderInPage value
|
||||
clusters.sort((a, b) => (a.orderInPage > b.orderInPage) ? 1 : -1);
|
||||
clusters.sort((a, b) => (a.orderInPage > b.orderInPage ? 1 : -1));
|
||||
|
||||
this.setState({
|
||||
clusters,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 401) {
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else if (!(error.hasOwnProperty("response") && error.response.status === 404)) {
|
||||
} else if (
|
||||
!(error.hasOwnProperty('response') && error.response.status === 404)
|
||||
) {
|
||||
// API sends 404 when no apps
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load clusters.",
|
||||
description: 'Error occurred while trying to load clusters.',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//fetch applications
|
||||
// fetch applications
|
||||
fetchApplications = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
this.setState({ loading: true });
|
||||
|
||||
const filters = {
|
||||
appType: "PUBLIC",
|
||||
deviceType: "android"
|
||||
appType: 'PUBLIC',
|
||||
deviceType: 'android',
|
||||
};
|
||||
|
||||
//send request to the invoker
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications",
|
||||
filters
|
||||
).then(res => {
|
||||
// send request to the invoker
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications',
|
||||
filters,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const applications = res.data.data.applications.map(application => {
|
||||
const release = application.applicationReleases[0];
|
||||
return {
|
||||
packageId: `app:${application.packageName}`,
|
||||
iconUrl: release.iconPath,
|
||||
name: application.name
|
||||
}
|
||||
name: application.name,
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({
|
||||
@ -239,101 +260,115 @@ class Page extends React.Component {
|
||||
applications,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load pages.",
|
||||
description: 'Error occurred while trying to load pages.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
toggleAddNewClusterVisibility = (isAddNewClusterVisible) => {
|
||||
toggleAddNewClusterVisibility = isAddNewClusterVisible => {
|
||||
this.setState({
|
||||
isAddNewClusterVisible
|
||||
isAddNewClusterVisible,
|
||||
});
|
||||
};
|
||||
|
||||
addSavedClusterToThePage = (cluster) => {
|
||||
addSavedClusterToThePage = cluster => {
|
||||
this.setState({
|
||||
clusters: [...this.state.clusters, cluster],
|
||||
isAddNewClusterVisible: false
|
||||
isAddNewClusterVisible: false,
|
||||
});
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
};
|
||||
|
||||
updateLinks = (links) =>{
|
||||
updateLinks = links => {
|
||||
this.setState({
|
||||
links
|
||||
links,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {pageName, loading, clusters, applications, isAddNewClusterVisible, links} = this.state;
|
||||
const {
|
||||
pageName,
|
||||
loading,
|
||||
clusters,
|
||||
applications,
|
||||
isAddNewClusterVisible,
|
||||
links,
|
||||
} = this.state;
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop:0, backgroundColor: "#fff"}}>
|
||||
<Breadcrumb style={{paddingBottom: 16}}>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
<Link to="/publisher/apps">
|
||||
<Icon type="home" /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Manage</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
Manage
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/manage/android-enterprise">Android Enterprise</Link>
|
||||
<Link to="/publisher/manage/android-enterprise">
|
||||
Android Enterprise
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Manage Page</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Manage Android Enterprise</h3>
|
||||
{/*<Paragraph>Lorem ipsum</Paragraph>*/}
|
||||
{/* <Paragraph>Lorem ipsum</Paragraph>*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<Spin spinning={loading}>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<Row>
|
||||
<Col md={8} sm={18} xs={24}>
|
||||
<Title editable={{onChange: this.updatePageName}} level={2}>{pageName}</Title>
|
||||
<Title editable={{ onChange: this.updatePageName }} level={2}>
|
||||
{pageName}
|
||||
</Title>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Title level={4}>Links</Title>
|
||||
{
|
||||
links.map(link => {
|
||||
{links.map(link => {
|
||||
if (this.pageNames.hasOwnProperty(link.toString())) {
|
||||
return <Tag key={link}
|
||||
color="#87d068">{this.pageNames[link.toString()]}</Tag>
|
||||
} else {
|
||||
return (
|
||||
<Tag key={link} color="#87d068">
|
||||
{this.pageNames[link.toString()]}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
}
|
||||
})}
|
||||
<EditLinks
|
||||
updateLinks={this.updateLinks}
|
||||
pageId={this.pageId}
|
||||
selectedLinks={links}
|
||||
pages={this.pages}/>
|
||||
pages={this.pages}
|
||||
/>
|
||||
</Col>
|
||||
{/*<Col>*/}
|
||||
{/* <Col>*/}
|
||||
|
||||
{/*</Col>*/}
|
||||
{/* </Col>*/}
|
||||
</Row>
|
||||
|
||||
<Divider dashed={true}/>
|
||||
<Divider dashed={true} />
|
||||
<Title level={4}>Clusters</Title>
|
||||
|
||||
<div hidden={isAddNewClusterVisible} style={{textAlign: "center"}}>
|
||||
<div
|
||||
hidden={isAddNewClusterVisible}
|
||||
style={{ textAlign: 'center' }}
|
||||
>
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="round"
|
||||
@ -342,25 +377,29 @@ class Page extends React.Component {
|
||||
onClick={() => {
|
||||
this.toggleAddNewClusterVisibility(true);
|
||||
}}
|
||||
>Add new cluster</Button>
|
||||
>
|
||||
Add new cluster
|
||||
</Button>
|
||||
</div>
|
||||
<div hidden={!isAddNewClusterVisible}>
|
||||
<Cluster
|
||||
cluster={{
|
||||
clusterId: 0,
|
||||
name: "New Cluster",
|
||||
products: []
|
||||
name: 'New Cluster',
|
||||
products: [],
|
||||
}}
|
||||
orderInPage={clusters.length}
|
||||
isTemporary={true}
|
||||
pageId={this.pageId}
|
||||
applications={applications}
|
||||
addSavedClusterToThePage={this.addSavedClusterToThePage}
|
||||
toggleAddNewClusterVisibility={this.toggleAddNewClusterVisibility}/>
|
||||
toggleAddNewClusterVisibility={
|
||||
this.toggleAddNewClusterVisibility
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
clusters.map((cluster, index) => {
|
||||
{clusters.map((cluster, index) => {
|
||||
return (
|
||||
<Cluster
|
||||
key={cluster.clusterId}
|
||||
@ -371,14 +410,13 @@ class Page extends React.Component {
|
||||
pageId={this.pageId}
|
||||
applications={applications}
|
||||
swapClusters={this.swapClusters}
|
||||
removeLoadedCluster={this.removeLoadedCluster}/>
|
||||
removeLoadedCluster={this.removeLoadedCluster}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ const isLocalhost = Boolean(
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
|
||||
),
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
@ -61,7 +61,7 @@ export function register(config) {
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@ -89,7 +89,7 @@ function registerValidSW(swUrl, config) {
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
@ -139,7 +139,7 @@ function checkValidServiceWorker(swUrl, config) {
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
'No internet connection found. App is running in offline mode.',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -17,22 +17,22 @@
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
const HtmlWebPackPlugin = require("html-webpack-plugin");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const configurations = require("./public/conf/config.json");
|
||||
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const configurations = require('./public/conf/config.json');
|
||||
|
||||
const config = {
|
||||
devtool: "source-map",
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
publicPath: '/publisher/'
|
||||
publicPath: '/publisher/',
|
||||
},
|
||||
watch: false,
|
||||
resolve: {
|
||||
alias: {
|
||||
AppData: path.resolve(__dirname, 'source/src/app/common/'),
|
||||
AppComponents: path.resolve(__dirname, 'source/src/app/components/')
|
||||
AppComponents: path.resolve(__dirname, 'source/src/app/components/'),
|
||||
},
|
||||
extensions: ['.jsx', '.js', '.ttf', '.woff', '.woff2', '.svg']
|
||||
extensions: ['.jsx', '.js', '.ttf', '.woff', '.woff2', '.svg'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@ -41,47 +41,47 @@ const config = {
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
]
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
use: [
|
||||
{
|
||||
loader: "html-loader",
|
||||
options: {minimize: true}
|
||||
}
|
||||
]
|
||||
loader: 'html-loader',
|
||||
options: { minimize: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader"]
|
||||
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
"css-loader",
|
||||
"postcss-loader",
|
||||
"sass-loader"
|
||||
]
|
||||
'css-loader',
|
||||
'postcss-loader',
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: ['style-loader', 'scss-loader']
|
||||
use: ['style-loader', 'scss-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
{
|
||||
loader: "style-loader"
|
||||
loader: 'style-loader',
|
||||
},
|
||||
{
|
||||
loader: "css-loader"
|
||||
loader: 'css-loader',
|
||||
},
|
||||
{
|
||||
loader: "less-loader",
|
||||
loader: 'less-loader',
|
||||
options: {
|
||||
modifyVars: {
|
||||
'primary-color': configurations.theme.primaryColor,
|
||||
@ -89,8 +89,8 @@ const config = {
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|svg)$/,
|
||||
@ -100,35 +100,35 @@ const config = {
|
||||
test: /\.(png|jpe?g)/i,
|
||||
use: [
|
||||
{
|
||||
loader: "url-loader",
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
name: "./img/[name].[ext]",
|
||||
limit: 10000
|
||||
}
|
||||
name: './img/[name].[ext]',
|
||||
limit: 10000,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: "img-loader"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
loader: 'img-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebPackPlugin({
|
||||
template: "./src/index.html",
|
||||
filename: "./index.html"
|
||||
template: './src/index.html',
|
||||
filename: './index.html',
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "[name].css",
|
||||
chunkFilename: "[id].css"
|
||||
})
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
'Config': JSON.stringify(require('./public/conf/config.json'))
|
||||
}
|
||||
Config: JSON.stringify(require('./public/conf/config.json')),
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
config.watch = true;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user