forked from community/device-mgt-core
parent
4943635a61
commit
52761c673d
@ -1,128 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
~
|
||||
~ Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||
<artifactId>application-mgt</artifactId>
|
||||
<version>4.1.11-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>org.wso2.carbon.device.application.mgt.publisher.ui</artifactId>
|
||||
<version>4.1.11-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>WSO2 Carbon - Application Management Publisher UI Component</name>
|
||||
<url>https://entgra.io</url>
|
||||
<description>This Component contains Application Management publisher UI</description>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<packagingExcludes>WEB-INF/lib/*cxf*.jar</packagingExcludes>
|
||||
<warName>publisher</warName>
|
||||
<webResources>
|
||||
<resource>
|
||||
<directory>${npm.output.directory}/dist</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${npm.output.directory}/public</directory>
|
||||
<targetPath>public</targetPath>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>${frontend.mave.version}</version>
|
||||
<configuration>
|
||||
<workingDirectory>${npm.working.dir}</workingDirectory>
|
||||
<!-- where to install npm -->
|
||||
<installDirectory>${npm.install.dir}</installDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<nodeVersion>${node.version}</nodeVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<!-- Optional configuration which provides for running any npm command -->
|
||||
<configuration>
|
||||
<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>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run-script ${npm.build.command}</arguments>
|
||||
</configuration>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>platform-windows</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>windows</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<!-- Override the executable names for Windows -->
|
||||
<npm.executable>npm.cmd</npm.executable>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
<properties>
|
||||
<maven.test.skip>false</maven.test.skip>
|
||||
<npm.executable>npm</npm.executable>
|
||||
<npm.build.command>build_prod</npm.build.command>
|
||||
<npm.working.dir>./react-app</npm.working.dir>
|
||||
<npm.install.dir>./react-app/tmp</npm.install.dir>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<npm.output.directory>react-app</npm.output.directory>
|
||||
</properties>
|
||||
</project>
|
@ -1,325 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
registry=http://nexus.entgra.io/repository/npm-group/
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"parser": "flow"
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
module.exports = function(api) {
|
||||
api.cache(true);
|
||||
const presets = ['@babel/preset-env', '@babel/preset-react'];
|
||||
const plugins = ['@babel/plugin-proposal-class-properties'];
|
||||
|
||||
return {
|
||||
presets,
|
||||
plugins,
|
||||
};
|
||||
};
|
@ -1,112 +0,0 @@
|
||||
{
|
||||
"name": "publisher",
|
||||
"version": "1.0.0",
|
||||
"description": "WSO2 IoT Server App Publisher",
|
||||
"main": "App.js",
|
||||
"proxy": "http://localhost:3001",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/wso2/carbon-devicemgt"
|
||||
},
|
||||
"license": "Apache License 2.0",
|
||||
"dependencies": {
|
||||
"@ant-design/compatible": "^1.0.0",
|
||||
"@ant-design/dark-theme": "^0.2.2",
|
||||
"@ant-design/icons": "^4.0.6",
|
||||
"@babel/polyfill": "^7.6.0",
|
||||
"acorn": "^6.2.0",
|
||||
"antd": "^4.0.0",
|
||||
"axios": "^0.19.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"d3": "^5.9.7",
|
||||
"dagre": "^0.8.4",
|
||||
"entgra-icons-react": "^1.0.0",
|
||||
"fetch": "^1.1.0",
|
||||
"imagemin": "^6.1.0",
|
||||
"keymirror": "^0.1.1",
|
||||
"lodash.debounce": "latest",
|
||||
"rc-tween-one": "^2.4.1",
|
||||
"react-d3-graph": "^2.1.0",
|
||||
"react-highlight-words": "^0.16.0",
|
||||
"react-html-parser": "^2.0.2",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-quill": "^1.3.3",
|
||||
"react-router": "latest",
|
||||
"react-router-config": "^5.0.1",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-scripts": "2.1.8",
|
||||
"react-star-ratings": "^2.3.0",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"shade-blend-color": "^1.0.0",
|
||||
"storm-react-diagrams": "^5.2.1",
|
||||
"typescript": "^3.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
||||
"@babel/preset-env": "^7.5.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/register": "^7.4.4",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"chai": "^4.1.2",
|
||||
"cross-env": "^7.0.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"entgra-icons": "^1.4.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-prettier": "4.3.0",
|
||||
"eslint-plugin-babel": "5.3.0",
|
||||
"eslint-plugin-import": "^2.21.2",
|
||||
"eslint-plugin-jsx": "0.0.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"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",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"img-loader": "^3.0.1",
|
||||
"json-loader": "^0.5.7",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^4.1.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mock-local-storage": "^1.0.5",
|
||||
"node-env-run": "^3.0.2",
|
||||
"node-sass": "^4.12.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"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",
|
||||
"react-redux": "^7.1.0",
|
||||
"redux": "^4.0.1",
|
||||
"sass-loader": "^6.0.7",
|
||||
"style-loader": "^0.18.2",
|
||||
"url-loader": "^1.1.2",
|
||||
"webpack": "^4.39.3",
|
||||
"webpack-cli": "^3.3.7",
|
||||
"webpack-dev-server": "^3.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --mode development --open",
|
||||
"dev": "webpack --mode development",
|
||||
"build": "webpack --mode production",
|
||||
"watch": "webpack --watch --mode development",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject",
|
||||
"build_prod": "cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=4096 webpack -p --display errors-only --hide-modules",
|
||||
"build_dev": "cross-env NODE_ENV=development webpack -d --watch ",
|
||||
"server": "node-env-run server --exec nodemon | pino-colada",
|
||||
"dev2": "run-p server start",
|
||||
"lint": "eslint \"src/**/*.js\""
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
{
|
||||
"appName": "publisher",
|
||||
"theme": {
|
||||
"logo": "https://entgra.io/assets/images/svg/logo.svg",
|
||||
"primaryColor": "rgb(24, 144, 255)",
|
||||
"footerText": "©2020 entgra.io"
|
||||
},
|
||||
"serverConfig": {
|
||||
"invoker": {
|
||||
"contextPath" : "/publisher-ui-request-handler",
|
||||
"uri": "/publisher-ui-request-handler/invoke",
|
||||
"publisher": "/application-mgt-publisher/v1.0",
|
||||
"store": "/application-mgt-store/v1.0",
|
||||
"deviceMgt": "/device-mgt/v1.0"
|
||||
},
|
||||
"loginUri": "/publisher-ui-request-handler/login",
|
||||
"logoutUri": "/publisher-ui-request-handler/logout",
|
||||
"ssoLoginUri": "/publisher-ui-request-handler/ssoLogin",
|
||||
"ssoLogoutUri": "/publisher-ui-request-handler/ssoLogout",
|
||||
"platform": "publisher",
|
||||
"appUiConfigUri": "/api/device-mgt-config/v1.0/configurations/ui-config"
|
||||
},
|
||||
"defaultPlatformIcons": {
|
||||
"default": {
|
||||
"icon": "fw-globe",
|
||||
"color": "#535c68"
|
||||
},
|
||||
"android": {
|
||||
"icon": "fw-android",
|
||||
"color": "#7db343"
|
||||
},
|
||||
"ios": {
|
||||
"icon": "fw-apple",
|
||||
"color": "#535c68"
|
||||
},
|
||||
"windows": {
|
||||
"icon": "fw-windows",
|
||||
"color": "#008cc4"
|
||||
}
|
||||
},
|
||||
"lifecycle": {
|
||||
"CREATED": {
|
||||
"title": "Created",
|
||||
"text": "The initial most state of an application.\n You can only proceed to one of the following states:",
|
||||
"icon": "fw-document",
|
||||
"step": 0
|
||||
},
|
||||
"IN-REVIEW": {
|
||||
"title": "In-Review",
|
||||
"text": "In this state the application is being reviewed by approvers. You can move from this state to APPROVED state if it is approved by reviewers. Otherwise, you can move the application into either REJECTED state or CREATED state based on the feedback getting by reviewers.",
|
||||
"icon": "fw-throttling-policy",
|
||||
"step": 1
|
||||
},
|
||||
"APPROVED": {
|
||||
"title": "Approved",
|
||||
"text": "The approved state is a compulsory state prior to publishing the application.\n You can only proceed to one of the following states:",
|
||||
"icon": "fw-check",
|
||||
"step": 2
|
||||
},
|
||||
"PUBLISHED": {
|
||||
"title": "Published",
|
||||
"text": "The state which is applied for applications which are qualified for your Corporate App Store. Only the applications of Published state can be installed to your corporate devices.\n You can only proceed to one of the following states:",
|
||||
"icon": "fw-globe",
|
||||
"step": 3
|
||||
},
|
||||
"BLOCKED": {
|
||||
"title": "Blocked",
|
||||
"text": "This state allows you to block your application either to publish or deprecate at a future date.\n You can only proceed to one of the following states:",
|
||||
"icon": "fw-lock",
|
||||
"step": 4
|
||||
},
|
||||
"DEPRECATED": {
|
||||
"title": "Deprecated",
|
||||
"text": "The applications which are outdated and no longer suit your app store.\n You can only proceed to one of the following states:",
|
||||
"icon": "fw-warning",
|
||||
"step": 5
|
||||
},
|
||||
"REJECTED": {
|
||||
"title": "Rejected",
|
||||
"text": "The Approvers can reject an application due to a faulty of the app or not being in compliance with company policies.\n You can only proceed to one of the following states:",
|
||||
"icon": "fw-error",
|
||||
"step": 6
|
||||
},
|
||||
"RETIRED": {
|
||||
"title": "Retired",
|
||||
"text": "The final state of an application, where no transition of states will be allowed after this.",
|
||||
"icon": "fw-delete",
|
||||
"step": 7
|
||||
}
|
||||
},
|
||||
"deviceTypes": {
|
||||
"mobileTypes": ["android", "ios", "windows"]
|
||||
},
|
||||
"windowsDeviceType": {
|
||||
"appType": ["msi", "appx"]
|
||||
},
|
||||
"windowsAppxMsiKeyValueForMetaData": {
|
||||
"metaKeyArray": ["Package_Url", "Dependency_Package_Url", "Certificate_Hash", "Encoded_Cert_Content", "Package_Family_Name", "Product_Id", "Content_Uri", "File_Hash"]
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "App Publisher",
|
||||
"name": "WSO2 IoT App Publisher",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/favicon.png",
|
||||
"sizes": "16x16",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 443 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 44 KiB |
@ -1,67 +0,0 @@
|
||||
{
|
||||
"Title" : "Title",
|
||||
"Description" : "Description",
|
||||
"ShortDescription" : "Short Description",
|
||||
"Category" : "Category",
|
||||
"Visibility" : "Visibility",
|
||||
"Devices" : "Devices",
|
||||
"Roles" : "Roles",
|
||||
"Groups" : "Groups",
|
||||
"Tags" : "Tags",
|
||||
"Platform" : "Platform",
|
||||
"Platforms" : "Platforms",
|
||||
"Applications": "Applications",
|
||||
"No.Platform" : "No Platforms",
|
||||
"Screenshots" : "Screenshots",
|
||||
"Icon" : "Icon",
|
||||
"Info" : "Info",
|
||||
"Banner" : "Banner",
|
||||
"Create.Application" : "Create Application",
|
||||
"Back" : "Back",
|
||||
"Cancel" : "Cancel",
|
||||
"Finish" : "Finish",
|
||||
"Continue" : "Continue",
|
||||
"Name" : "Name",
|
||||
"Application.Name" : "Application Name",
|
||||
"General" : "General",
|
||||
"App.Releases" : "Application Releases",
|
||||
"Package.Manager" : "Package Manager",
|
||||
"Save" : "Save",
|
||||
"Create.Release" : "Create Release",
|
||||
"Release.Channel" : "Release Channel",
|
||||
"Release" : "Release",
|
||||
"New.Release.For" : "New Release for",
|
||||
"Upload.Package.File" : "Upload Package File",
|
||||
"Upload" : "Upload",
|
||||
"Select.from.package.library" : "Select from package library",
|
||||
"Release.Name" : "Release Name",
|
||||
"Release.Notes" : "Release Notes",
|
||||
"Send.for.Review" : "Send for Review",
|
||||
"Production.Releases" : "Production Releases",
|
||||
"Beta.Releases" : "Beta Releases",
|
||||
"Alpha.Releases" : "Alpha Releases",
|
||||
"Version" : "Version",
|
||||
"Status" : "Status",
|
||||
"App.Publisher" : "Application Publisher",
|
||||
"Search.Apps" : "Search for Applications",
|
||||
"View.In.Store" : "View in Store",
|
||||
"Last.Updated" : "Last updated on",
|
||||
"Installs" : "Installs",
|
||||
"General.Info" : "General Info",
|
||||
"Select.Platform": "Select Platform",
|
||||
"Add.Release" : "Add Release to Application",
|
||||
"Share.With.Tenants" : "Share with Tenants",
|
||||
"Disable" : "Disable",
|
||||
"File.Based" : "File Based",
|
||||
"Activate" : "Activate",
|
||||
"Yes" : "Yes",
|
||||
"No" : "No",
|
||||
"No.Platform.Tags" : "No Platform Tags",
|
||||
"Create.Platform" : "Create Platform",
|
||||
"Optional": "Optional",
|
||||
"Identifier": "Identifier",
|
||||
"Next": "Next",
|
||||
"Platform.Enable": "Enable Platform",
|
||||
"Share.with.Tenants": "Share between all tenants",
|
||||
"Platform.Properties": "Platform Properties"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,903 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto-Medium";
|
||||
src: url('../../fonts/Roboto-Medium.woff');
|
||||
src: local("Roboto-Medium"), url("../../fonts/Roboto-Medium.ttf") format("ttf");
|
||||
src: local("Roboto-Medium"), url("../../fonts/Roboto-Medium.woff") format("woff");
|
||||
src: local("Roboto-Medium"), url("../../fonts/Roboto-Medium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto-Regular";
|
||||
src: url("../../fonts/Roboto-Regular.woff");
|
||||
src: local("Roboto-Regular"), url("../../fonts/Roboto-Regular.ttf") format("ttf");
|
||||
src: local("Roboto-Regular"), url("../../fonts/Roboto-Regular.woff") format("woff");
|
||||
src: local("Roboto-Regular"), url("../../fonts/Roboto-Regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/*Colors*/
|
||||
.primary {
|
||||
color: white;
|
||||
background-color: #2196f3 !important;
|
||||
}
|
||||
|
||||
.primary-flat {
|
||||
color: #2196F3 !important;
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: white;
|
||||
background-color: #e91e63 !important;
|
||||
}
|
||||
|
||||
.danger-flat {
|
||||
color: #e91e63 !important;
|
||||
}
|
||||
|
||||
.grey {
|
||||
color: #b3b3b3 !important;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
/* Custom button styles based on material design specs. */
|
||||
|
||||
.custom-raised {
|
||||
font-family: Roboto-Medium;
|
||||
text-transform: uppercase !important;
|
||||
font-size: 14px !important;
|
||||
padding-left: 16px !important;
|
||||
border-radius: 2px !important;
|
||||
padding-right: 16px !important;
|
||||
height: 36px !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.custom-raised:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
|
||||
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
|
||||
background-color: #1976D2 !important;
|
||||
}
|
||||
|
||||
.custom-raised:focus {
|
||||
box-shadow: none !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
background-color: #1976D2 !important;
|
||||
}
|
||||
|
||||
.custom-flat {
|
||||
font-family: Roboto-Medium;
|
||||
height: 36px !important;
|
||||
border-radius: 2px !important;
|
||||
margin-left: 8px !important;
|
||||
margin-right: 8px !important;
|
||||
padding-left: 8px !important;
|
||||
padding-right: 8px !important;
|
||||
background-color: transparent !important;
|
||||
text-transform: uppercase;
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.custom-flat:hover {
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.12) !important;
|
||||
}
|
||||
|
||||
.custom-flat:focus {
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: rgba(0, 0, 0, 0.40) !important;
|
||||
}
|
||||
|
||||
.circle-button {
|
||||
border-radius: 100% !important;
|
||||
height: 36px !important;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
/* Body Styling */
|
||||
body {
|
||||
width: 100%;
|
||||
font-family: "Roboto-Regular" !important;
|
||||
font-size: 14px !important;
|
||||
background-color: #e8e8e8 !important;
|
||||
}
|
||||
|
||||
.app-manager-title {
|
||||
font-family: "Roboto-Medium";
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.app-manager-sub-title {
|
||||
font-family: "Roboto-Regular";
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#app-mgt-footer {
|
||||
clear: both;
|
||||
position: relative;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: #334d88;
|
||||
}
|
||||
|
||||
/* Login page styles*/
|
||||
#userName {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#password {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
background-color: #3f50b5;
|
||||
color: white;
|
||||
height: 128px;
|
||||
width: 100%;
|
||||
margin: 0 !important;
|
||||
padding: 20px;
|
||||
box-shadow: -2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
|
||||
#login-card {
|
||||
width: 25%;
|
||||
height: 50%;
|
||||
margin: 10% auto;
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 14px;
|
||||
border-radius: 0;
|
||||
background-color: #ffffff;
|
||||
box-shadow: -2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
|
||||
.login-header-title {
|
||||
font-family: Roboto-Medium;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.login-header-logo {
|
||||
height: 70px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
margin: 0 !important;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
/* Base layout container */
|
||||
|
||||
/* Base layout header content*/
|
||||
.header-content {
|
||||
height: 128px !important;
|
||||
width: 100% !important;
|
||||
margin: 0 10px 0 0;
|
||||
background-color: #3f50b5 !important;
|
||||
position: fixed; /* Set the navbar to fixed position */
|
||||
top: 0; /* Position the navbar at the top of the page */
|
||||
z-index: 2;
|
||||
box-shadow: -2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
|
||||
/* Contains the header styles.*/
|
||||
.header {
|
||||
padding: 24px 24px 10px 24px;
|
||||
/*margin: 16px 16px 20px 16px;*/
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#header-text {
|
||||
color: #ffffff;
|
||||
font-size: 20px;
|
||||
font-family: Roboto-Medium;
|
||||
top: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* The buttons in the header (User and Notification)*/
|
||||
.header-button-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-user-name {
|
||||
font-family: Roboto-Medium;
|
||||
font-size: 14px;
|
||||
padding-top: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header-image {
|
||||
height: 43px;
|
||||
width: 100px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
#header-button {
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#header-button:hover {
|
||||
background-color: #4353bd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#header-button i {
|
||||
position: absolute;
|
||||
bottom: 19px;
|
||||
left: 17px;
|
||||
}
|
||||
|
||||
.btn-header {
|
||||
margin-top: 15px;
|
||||
margin-right: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#sub-title {
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
padding-top: 5px;
|
||||
padding-left: 18px;
|
||||
color: RGBA(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/* Search box styles */
|
||||
.search-box {
|
||||
display: flex;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.search-box i {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
color: #BaBaBa;
|
||||
}
|
||||
|
||||
#search {
|
||||
position: relative;
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
left: 15px;
|
||||
top: 0px;
|
||||
height: 25px;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 0%;
|
||||
}
|
||||
|
||||
/* Application Add button */
|
||||
#add-btn-container {
|
||||
position: absolute;
|
||||
top: 98px;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
background-color: #ff5722;
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
background-color: #E64A19;
|
||||
}
|
||||
|
||||
#sub-title-container {
|
||||
height: 100px;
|
||||
padding: 50px 0 20px 0;
|
||||
}
|
||||
|
||||
.application-container {
|
||||
padding: 0 !important;
|
||||
min-height: 100% !important;
|
||||
margin-top: 128px !important;
|
||||
}
|
||||
|
||||
/* Holds the app publisher pages. */
|
||||
.publisher-card {
|
||||
height: auto;
|
||||
background-color: white;
|
||||
box-shadow: 2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.platform-link-placeholder {
|
||||
color: #888888;
|
||||
float: right;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.platform-link-placeholder i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.application-list {
|
||||
transition: margin-right .5s;
|
||||
}
|
||||
|
||||
#batch-content {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.app-list-icon {
|
||||
border-radius: 50%;
|
||||
height: 50px;
|
||||
width: 50px
|
||||
}
|
||||
|
||||
.app-table-row {
|
||||
height: 62px;
|
||||
cursor: pointer;
|
||||
padding-top: 6px;
|
||||
font-family: "Roboto-Regular";
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
.app-table-row:hover {
|
||||
color: white;
|
||||
background-color: #3f50b5;
|
||||
}
|
||||
|
||||
.app-list-table-header {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
font-family: "Roboto-Medium";
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.app-view-image {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
#app-visibility-default {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#app-image-screenshot {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
#app-image-icon {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
#app-image-banner {
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
#form-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.application-create-banner-dropzone {
|
||||
width: 300px;
|
||||
height: 150px;
|
||||
border-radius: 5%;
|
||||
position: relative;
|
||||
border: dashed #888888 2px;
|
||||
}
|
||||
|
||||
.application-create-banner-dropzone i {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 145px;
|
||||
}
|
||||
|
||||
.application-create-screenshot-dropzone {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: 0 5px 0 5px;
|
||||
border-radius: 10%;
|
||||
position: relative;
|
||||
border: dashed #888888 2px;
|
||||
}
|
||||
|
||||
.application-create-screenshot-dropzone i {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 65px;
|
||||
}
|
||||
|
||||
.application-create-icon-dropzone {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 10%;
|
||||
position: relative;
|
||||
border: dashed #888888 2px;
|
||||
}
|
||||
|
||||
.application-create-icon-dropzone i {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 65px;
|
||||
}
|
||||
|
||||
#screenshot-container {
|
||||
max-width: 600px;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#app-icon-container {
|
||||
height: 300px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#modal-body-content {
|
||||
max-height: 700px;
|
||||
padding-left: 24px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.custom-footer {
|
||||
justify-content: inherit !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.footer-main-btn {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#img-btn-screenshot {
|
||||
margin: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
#app-create-modal {
|
||||
max-width: 850px;
|
||||
border-radius: 0% !important;
|
||||
}
|
||||
|
||||
.app-create-modal-header {
|
||||
background-color: #4353bd;
|
||||
color: white;
|
||||
padding: 24px !important;
|
||||
}
|
||||
|
||||
.app-create-modal-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#store {
|
||||
border: none;
|
||||
border-bottom: solid #BDBDBD 1px;
|
||||
border-radius: 0px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#version {
|
||||
border: none;
|
||||
border-bottom: solid #BDBDBD 1px;
|
||||
border-radius: 0px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#app-release-switch-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#app-release-switch-label {
|
||||
position: absolute;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#app-release-switch {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.image-sub-title {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: #818181;
|
||||
}
|
||||
|
||||
/* Application View */
|
||||
|
||||
#application-view-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#application-view-row {
|
||||
margin: 10px 10px 0 20px;
|
||||
}
|
||||
|
||||
#app-icon {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
border: solid 1px black;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.app-updated-date {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.app-install-count {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.app-details-tbl {
|
||||
outline: none;
|
||||
border-color: #2196F3;
|
||||
}
|
||||
|
||||
.app-details-tbl tr {
|
||||
margin: 20px 0 0 0;
|
||||
}
|
||||
|
||||
.app-details-tbl td {
|
||||
margin-left: 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* Application Edit Base Layout */
|
||||
|
||||
#application-edit-header {
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.application-header-text {
|
||||
margin: 10px 0px 0px 10px;
|
||||
}
|
||||
|
||||
#save-btn-content {
|
||||
float: right;
|
||||
|
||||
}
|
||||
|
||||
#app-save-btn {
|
||||
border-radius: 0%;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
margin: 5px 5px 5px 0px;
|
||||
height: 70%;
|
||||
width: 50%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.save-btn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*Tab styling*/
|
||||
|
||||
div.tab {
|
||||
float: left;
|
||||
border-right: 1px solid #d8d8d8;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Style the tab buttons */
|
||||
|
||||
div.tab button {
|
||||
display: block;
|
||||
background-color: inherit;
|
||||
color: black;
|
||||
padding: 15px 16px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
/* Change background color of buttons on hover */
|
||||
|
||||
div.tab button:hover {
|
||||
background-color: #ddd6d7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Create an active/current "tab button" class */
|
||||
|
||||
div.tab button.active {
|
||||
background-color: #1b3bcc;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#application-edit-main-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#application-edit-outer-content {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#app-edit-content {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.back-to-app {
|
||||
position: absolute;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.back-to-app i {
|
||||
padding: 12px 10px 10px 12px;
|
||||
}
|
||||
|
||||
.back-to-app:hover {
|
||||
cursor: pointer;
|
||||
background-color: #dedede;
|
||||
transition: .5s;
|
||||
}
|
||||
|
||||
/* Create Release and Release management */
|
||||
|
||||
.release-header {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.release-create {
|
||||
height: 150px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.release-detail-content {
|
||||
width: 100%;
|
||||
margin-top: 20%;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.form-btn {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.release-content {
|
||||
height: 180px;
|
||||
width: 95%;
|
||||
border: dashed 1px #626262;
|
||||
border-radius: 2%;
|
||||
position: relative;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
.release-content:after {
|
||||
content: "";
|
||||
letter-spacing: 4px;
|
||||
}
|
||||
|
||||
.release {
|
||||
margin: 30px 10px 20px 30px;
|
||||
}
|
||||
|
||||
.no-release-content {
|
||||
position: absolute;
|
||||
margin-top: 10px;
|
||||
left: 40%;
|
||||
}
|
||||
|
||||
.button-add:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.release-inner {
|
||||
margin-top: 5%;
|
||||
}
|
||||
|
||||
/* Application Edit General Info */
|
||||
|
||||
.app-edit-general-info {
|
||||
margin-top: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.save-info {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.app-view-field {
|
||||
font-family: Roboto-Medium;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.app-view-text {
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Platform Specific Styles. */
|
||||
#platform-listing {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.create-platform i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#platform-list {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.platform-content {
|
||||
margin: 10px;
|
||||
padding-top: 16px;
|
||||
box-shadow: 2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
|
||||
.platform-content .row {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.platform-content .col {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.platform-content-basic {
|
||||
padding: 0 16px 0 16px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.platform-content-more-outer {
|
||||
|
||||
}
|
||||
|
||||
.platform-content-more {
|
||||
padding: 16px 16px 24px 16px;
|
||||
}
|
||||
|
||||
.platform-content-footer {
|
||||
display: flex;
|
||||
padding: 8px 8px 8px 8px;
|
||||
}
|
||||
|
||||
.platform-text-container {
|
||||
padding: 8px 16px 0 16px;
|
||||
}
|
||||
|
||||
.circle-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.platform-icon-letter {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-family: Roboto-Medium;
|
||||
font-size: 70px;
|
||||
color: white;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.platform-icon-container {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
background-color: #01579B;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
|
||||
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
|
||||
.platform-property-container {
|
||||
padding-top: 20px;
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.platform-property-row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.circle-btn-clear {
|
||||
background-color: white !important;
|
||||
color: rgba(0, 0, 0, 0.50) !important;
|
||||
}
|
||||
|
||||
.circle-btn-clear:hover {
|
||||
background-color: white !important;
|
||||
color: rgba(0, 0, 0, 0.38) !important;
|
||||
}
|
||||
|
||||
.circle-btn-clear:focus {
|
||||
background-color: white !important;
|
||||
color: rgba(0, 0, 0, 0.60) !important;
|
||||
}
|
||||
|
||||
.data-table-row-cell {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
text-align: center;
|
||||
font-family: Roboto-Medium;
|
||||
font-weight: 800;
|
||||
font-size: 15em;
|
||||
color: #BaBaBa;
|
||||
}
|
||||
|
||||
.error-code p {
|
||||
|
||||
}
|
||||
|
||||
.error-text {
|
||||
text-align: center;
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
.circle-btn-add {
|
||||
background-color: #bababa !important;
|
||||
border-radius: 50% !important;
|
||||
height: 30px !important;
|
||||
width: 30px;
|
||||
text-align: -webkit-center;
|
||||
font-size: 18px;
|
||||
padding: 6px !important;
|
||||
}
|
||||
|
||||
.circle-btn-add:hover {
|
||||
background-color: #828282 !important;
|
||||
}
|
||||
|
||||
/**
|
||||
If you need to change the color of active steps in stepper,
|
||||
uncomment the following and set the background color and font color as needed.
|
||||
*/
|
||||
/*
|
||||
.stepper-active-index {
|
||||
background-color: #0a6eff !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.stepper-passed-index {
|
||||
background-color: #0a6eff !important;
|
||||
color: green !important;
|
||||
}
|
||||
*/
|
@ -1,17 +0,0 @@
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const pino = require('express-pino-logger')();
|
||||
|
||||
const app = express();
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(pino);
|
||||
|
||||
app.get('/api/greeting', (req, res) => {
|
||||
const name = req.query.name || 'World';
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(JSON.stringify({ greeting: `Hello ${name}!` }));
|
||||
});
|
||||
|
||||
app.listen(3001, () =>
|
||||
console.log('Express server is running on localhost:3001')
|
||||
);
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.ant-upload.ant-upload-drag {
|
||||
height: 170px;
|
||||
}
|
||||
|
||||
.release .release-icon{
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.release .release-icon img{
|
||||
width: 100%;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.release .release-title{
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.release .release-screenshot img{
|
||||
height:450px;
|
||||
border-radius: 25px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.release .release-images{
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.release .release-images::-webkit-scrollbar-track{
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #F5F5F5;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.release .release-images::-webkit-scrollbar{
|
||||
height: 8px;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.release .release-images::-webkit-scrollbar-thumb{
|
||||
background-color: #aaaaaa;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.release .release-screenshot{
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.main-container{
|
||||
background: #f0f2f5;
|
||||
min-height: 780px
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
|
||||
.main-container{
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import '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 './components/ConfigContext';
|
||||
import { getUiConfig } from './services/utils/uiConfigHandler';
|
||||
|
||||
const { Content } = Layout;
|
||||
const loadingView = (
|
||||
<Layout>
|
||||
<Content
|
||||
style={{
|
||||
padding: '0 0',
|
||||
paddingTop: 300,
|
||||
backgroundColor: '#fff',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Spin tip="Loading..." />
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
const errorView = (
|
||||
<Result
|
||||
style={{
|
||||
paddingTop: 200,
|
||||
}}
|
||||
status="500"
|
||||
title="Error occurred while loading the configuration"
|
||||
subTitle="Please refresh your browser window"
|
||||
/>
|
||||
);
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
error: false,
|
||||
config: {},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateFavicon();
|
||||
axios
|
||||
.get(window.location.origin + '/publisher/public/conf/config.json')
|
||||
.then(res => {
|
||||
const config = res.data;
|
||||
this.checkUserLoggedIn(config);
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
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 => {
|
||||
config.androidEnterpriseToken = res.data.data.token;
|
||||
this.setState({
|
||||
loading: false,
|
||||
config: config,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
config.androidEnterpriseToken = null;
|
||||
this.setState({
|
||||
loading: false,
|
||||
config: config,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
checkUserLoggedIn = config => {
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.contextPath +
|
||||
'/user',
|
||||
)
|
||||
.then(res => {
|
||||
config.user = {
|
||||
username: 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/';
|
||||
} else {
|
||||
this.getUserPermissions(config);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.handleApiError(error, config);
|
||||
});
|
||||
};
|
||||
|
||||
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';
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
};
|
||||
|
||||
getUserPermissions = config => {
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.deviceMgt +
|
||||
'/users/current-user/permissions',
|
||||
)
|
||||
.then(res => {
|
||||
config.user.permissions = res.data.data.permissions;
|
||||
this.getAndroidEnterpriseToken(config);
|
||||
})
|
||||
.catch(error => {
|
||||
this.handleApiError(error, config);
|
||||
});
|
||||
};
|
||||
|
||||
handleApiError = (error, config) => {
|
||||
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);
|
||||
getUiConfig(config).then(uiConfig => {
|
||||
if (uiConfig !== undefined) {
|
||||
if (uiConfig.isSsoEnable) {
|
||||
window.location =
|
||||
window.location.origin +
|
||||
config.serverConfig.ssoLoginUri +
|
||||
'?redirect=' +
|
||||
window.location.origin +
|
||||
pageURL;
|
||||
} else if (lastURLSegment !== 'login') {
|
||||
window.location.href =
|
||||
window.location.origin +
|
||||
`/${config.appName}/login?redirect=${redirectUrl}`;
|
||||
} else {
|
||||
this.getAndroidEnterpriseToken(config);
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
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 => (
|
||||
<RouteWithSubRoutes key={route.path} {...route} />
|
||||
))}
|
||||
</Switch>
|
||||
</div>
|
||||
</ConfigContext.Provider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loading && loadingView}
|
||||
{!loading && !error && applicationView}
|
||||
{error && errorView}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import react from 'react';
|
||||
import { withConfigContext } from '../ConfigContext';
|
||||
import { isAuthorized } from '../../services/utils/authorizationHandler';
|
||||
|
||||
class Authorized extends react.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return isAuthorized(this.props.context.user, this.props.permission)
|
||||
? this.props.yes
|
||||
: this.props.no;
|
||||
}
|
||||
}
|
||||
|
||||
Authorized.defaultProps = {
|
||||
yes: null,
|
||||
no: null,
|
||||
};
|
||||
export default withConfigContext(Authorized);
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const ConfigContext = React.createContext();
|
||||
|
||||
export const withConfigContext = Component => {
|
||||
// eslint-disable-next-line react/display-name
|
||||
return props => (
|
||||
<ConfigContext.Consumer>
|
||||
{context => {
|
||||
return <Component {...props} context={context} />;
|
||||
}}
|
||||
</ConfigContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigContext;
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
class RouteWithSubRoutes extends React.Component {
|
||||
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} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RouteWithSubRoutes;
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.App {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.ant-layout-header{
|
||||
padding: 0;
|
||||
height: auto;
|
||||
box-shadow: 0 2px 8px #f0f1f2;
|
||||
}
|
||||
|
||||
.steps-content {
|
||||
margin-top: 16px;
|
||||
border: 1px dashed #e9e9e9;
|
||||
border-radius: 6px;
|
||||
background-color: #fafafa;
|
||||
min-height: 200px;
|
||||
text-align: center;
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
.steps-action {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.ant-input-affix-wrapper .ant-input{
|
||||
min-height: 0;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
~
|
||||
~ Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Entgra App Publisher</title>
|
||||
<script src="https://apis.google.com/js/client.js"></script>
|
||||
</head>
|
||||
<div id="root"></div>
|
||||
</html>
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as serviceWorker from './services/serviceWorkers/serviceWorker';
|
||||
import App from './App';
|
||||
import Login from './scenes/Login';
|
||||
import Dashboard from './scenes/Home';
|
||||
import Apps from './scenes/Home/scenes/Apps';
|
||||
import Release from './scenes/Home/scenes/Apps/scenes/Release';
|
||||
import AddNewEnterpriseApp from './scenes/Home/scenes/AddNewApp/scenes/Enterprise';
|
||||
import Mange from './scenes/Home/scenes/Manage';
|
||||
import './index.css';
|
||||
import AddNewPublicApp from './scenes/Home/scenes/AddNewApp/scenes/Public';
|
||||
import AddNewWebClip from './scenes/Home/scenes/AddNewApp/scenes/WebClip';
|
||||
import AddNewRelease from './scenes/Home/scenes/AddNewRelease';
|
||||
import AddNewCustomApp from './scenes/Home/scenes/AddNewApp/scenes/Custom';
|
||||
import ManageAndroidEnterprise from './scenes/Home/scenes/Manage/scenes/AndroidEnterprise';
|
||||
import Page from './scenes/Home/scenes/Manage/scenes/AndroidEnterprise/scenes/Page';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/publisher/login',
|
||||
exact: true,
|
||||
component: Login,
|
||||
},
|
||||
{
|
||||
path: '/publisher/',
|
||||
exact: false,
|
||||
component: Dashboard,
|
||||
routes: [
|
||||
{
|
||||
path: '/publisher/apps',
|
||||
component: Apps,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/apps/releases/:uuid',
|
||||
exact: true,
|
||||
component: Release,
|
||||
},
|
||||
{
|
||||
path: '/publisher/apps/:deviceType/:appId/add-release',
|
||||
component: AddNewRelease,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/enterprise',
|
||||
component: AddNewEnterpriseApp,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/public',
|
||||
component: AddNewPublicApp,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/web-clip',
|
||||
component: AddNewWebClip,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/add-new-app/custom-app',
|
||||
component: AddNewCustomApp,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/manage',
|
||||
component: Mange,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/manage/android-enterprise',
|
||||
component: ManageAndroidEnterprise,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: '/publisher/manage/android-enterprise/pages/:pageName/:pageId',
|
||||
component: Page,
|
||||
exact: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
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.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
Before Width: | Height: | Size: 2.6 KiB |
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { LogoutOutlined } from '@ant-design/icons';
|
||||
import { notification, Menu } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../components/ConfigContext';
|
||||
import { getUiConfig } from '../../../../services/utils/uiConfigHandler';
|
||||
|
||||
/*
|
||||
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,
|
||||
};
|
||||
}
|
||||
/*
|
||||
This function call the logout api when the request is success
|
||||
*/
|
||||
handleSubmit = () => {
|
||||
const thisForm = this;
|
||||
const config = this.props.context;
|
||||
|
||||
thisForm.setState({
|
||||
inValid: false,
|
||||
});
|
||||
|
||||
let logoutUri;
|
||||
getUiConfig(config).then(uiConfig => {
|
||||
if (uiConfig !== undefined) {
|
||||
if (uiConfig.isSsoEnable) {
|
||||
logoutUri = window.location.origin + config.serverConfig.ssoLogoutUri;
|
||||
} else {
|
||||
logoutUri = window.location.origin + config.serverConfig.logoutUri;
|
||||
}
|
||||
axios
|
||||
.post(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 + `/${config.appName}/login`;
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description: 'Error occurred while trying to logout.',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Item key="1" onClick={this.handleSubmit}>
|
||||
<LogoutOutlined />
|
||||
Logout
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(Logout);
|
@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
AndroidFilled,
|
||||
AppstoreOutlined,
|
||||
ControlOutlined,
|
||||
PlusOutlined,
|
||||
SettingOutlined,
|
||||
UserOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { Layout, Menu, Drawer, Button } from 'antd';
|
||||
import { Switch, Link } from 'react-router-dom';
|
||||
import RouteWithSubRoutes from '../../components/RouteWithSubRoutes';
|
||||
import { Redirect } from 'react-router';
|
||||
import './styles.css';
|
||||
import { withConfigContext } from '../../components/ConfigContext';
|
||||
import Logout from './components/Logout';
|
||||
import { isAuthorized } from '../../services/utils/authorizationHandler';
|
||||
|
||||
const { Header, Content, Footer } = Layout;
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
routes: props.routes,
|
||||
visible: false,
|
||||
collapsed: false,
|
||||
};
|
||||
this.config = this.props.context;
|
||||
this.Logo = this.config.theme.logo;
|
||||
this.footerText = this.config.theme.footerText;
|
||||
}
|
||||
|
||||
showMobileNavigationBar = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
collapsed: !this.state.collapsed,
|
||||
});
|
||||
};
|
||||
|
||||
onCloseMobileNavigationBar = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Layout>
|
||||
<Header
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
>
|
||||
<div className="logo-image">
|
||||
<Link to="/publisher/apps">
|
||||
<img alt="logo" src={this.Logo} />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="web-layout">
|
||||
<Menu
|
||||
theme="light"
|
||||
mode="horizontal"
|
||||
defaultSelectedKeys={['1']}
|
||||
style={{ lineHeight: '64px' }}
|
||||
>
|
||||
<Menu.Item key="1">
|
||||
<Link to="/publisher/apps">
|
||||
<AppstoreOutlined />
|
||||
Apps
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{isAuthorized(
|
||||
this.props.context.user,
|
||||
'/permission/admin/app-mgt/publisher/application/update',
|
||||
) && (
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<PlusOutlined />
|
||||
Add New App
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="add-new-public-app">
|
||||
<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">
|
||||
Enterprise App
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="add-new-web-clip">
|
||||
<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">
|
||||
Custom App
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
)}
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<ControlOutlined />
|
||||
Manage
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="manage">
|
||||
<Link to="/publisher/manage">
|
||||
<SettingOutlined /> General
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{this.config.androidEnterpriseToken != null && (
|
||||
<Menu.Item key="manage-android-enterprise">
|
||||
<Link to="/publisher/manage/android-enterprise">
|
||||
<AndroidFilled /> Android Enterprise
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</SubMenu>
|
||||
|
||||
<SubMenu
|
||||
className="profile"
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<UserOutlined />
|
||||
{this.config.username}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Logout />
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</div>
|
||||
</Header>
|
||||
</Layout>
|
||||
|
||||
<Layout className="mobile-layout">
|
||||
<div className="mobile-menu-button">
|
||||
<Button type="link" onClick={this.showMobileNavigationBar}>
|
||||
{this.state.collapsed ? (
|
||||
<MenuFoldOutlined />
|
||||
) : (
|
||||
<MenuUnfoldOutlined />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Layout>
|
||||
<Drawer
|
||||
title={
|
||||
<Link
|
||||
to="/publisher/apps"
|
||||
onClick={this.onCloseMobileNavigationBar}
|
||||
>
|
||||
<img
|
||||
alt="logo"
|
||||
src={this.Logo}
|
||||
style={{ marginLeft: 30 }}
|
||||
width={'60%'}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
placement="left"
|
||||
closable={false}
|
||||
onClose={this.onCloseMobileNavigationBar}
|
||||
visible={this.state.visible}
|
||||
getContainer={false}
|
||||
style={{ position: 'absolute' }}
|
||||
>
|
||||
<Menu
|
||||
theme="light"
|
||||
mode="inline"
|
||||
defaultSelectedKeys={['1']}
|
||||
style={{ lineHeight: '64px', width: 231 }}
|
||||
onClick={this.onCloseMobileNavigationBar}
|
||||
>
|
||||
<Menu.Item key="1">
|
||||
<Link to="/publisher/apps">
|
||||
<AppstoreOutlined />
|
||||
Apps
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<PlusOutlined />
|
||||
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>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="setting:3">
|
||||
<Link to="/publisher/add-new-app/web-clip">Web Clip</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="setting:4">
|
||||
<Link to="/publisher/add-new-app/custom-app">Custom App</Link>
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
<Menu.Item key="2">
|
||||
<Link to="/publisher/manage">
|
||||
<ControlOutlined />
|
||||
Manage
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Drawer>
|
||||
<Layout className="mobile-layout">
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
defaultSelectedKeys={['1']}
|
||||
style={{ lineHeight: '63px', position: 'fixed', marginLeft: '80%' }}
|
||||
>
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="submenu-title-wrapper">
|
||||
<UserOutlined />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Logout />
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</Layout>
|
||||
|
||||
<Layout className="dashboard-body">
|
||||
<Content style={{ marginTop: 2 }}>
|
||||
<Switch>
|
||||
<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>
|
||||
</Layout>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(Dashboard);
|
@ -1,554 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import { Alert, Button, Col, Input, Row, Select, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
|
||||
import debounce from 'lodash.debounce';
|
||||
import Authorized from '../../../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 5 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 19 },
|
||||
},
|
||||
};
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
|
||||
class NewAppDetailsForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
categories: [],
|
||||
tags: [],
|
||||
deviceTypes: [],
|
||||
selectedValue: null,
|
||||
fetching: false,
|
||||
roleSearchValue: [],
|
||||
unrestrictedRoles: [],
|
||||
};
|
||||
this.lastFetchId = 0;
|
||||
this.fetchRoles = debounce(this.fetchRoles, 800);
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const { formConfig } = this.props;
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
categories,
|
||||
tags,
|
||||
unrestrictedRoles,
|
||||
} = values;
|
||||
const unrestrictedRolesData = [];
|
||||
unrestrictedRoles.map(val => {
|
||||
unrestrictedRolesData.push(val.key);
|
||||
});
|
||||
const application = {
|
||||
name,
|
||||
description,
|
||||
categories,
|
||||
tags,
|
||||
unrestrictedRoles: unrestrictedRolesData,
|
||||
};
|
||||
|
||||
if (formConfig.installationType !== 'WEB_CLIP') {
|
||||
application.deviceType = values.deviceType;
|
||||
} else {
|
||||
application.type = 'WEB_CLIP';
|
||||
application.deviceType = 'ALL';
|
||||
}
|
||||
this.validateAppName(name, application.deviceType, application);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
validateAppName = (name, deviceType, application) => {
|
||||
const config = this.props.context;
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
`/applications/device-type/${deviceType}/app-name/${name}`,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
this.props.onSuccessApplicationData(application);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.hasOwnProperty('response') && error.response.status === 403) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
} else if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 409
|
||||
) {
|
||||
this.props.form.setFields({
|
||||
name: {
|
||||
value: name,
|
||||
errors: [
|
||||
new Error('App name already exists, Please try another'),
|
||||
],
|
||||
},
|
||||
});
|
||||
} else {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to validate app name.',
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.getCategories();
|
||||
this.getTags();
|
||||
this.getDeviceTypes();
|
||||
}
|
||||
|
||||
getCategories = () => {
|
||||
const config = this.props.context;
|
||||
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,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
let tags = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
tags: tags,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load tags.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
getDeviceTypes = () => {
|
||||
const config = this.props.context;
|
||||
const { formConfig } = this.props;
|
||||
const { installationType } = formConfig;
|
||||
|
||||
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') {
|
||||
allDeviceTypes.forEach(deviceType => {
|
||||
if (!mobileDeviceTypes.includes(deviceType.name)) {
|
||||
allowedDeviceTypes.push(deviceType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
allDeviceTypes.forEach(deviceType => {
|
||||
if (mobileDeviceTypes.includes(deviceType.name)) {
|
||||
allowedDeviceTypes.push(deviceType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
deviceTypes: allowedDeviceTypes,
|
||||
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;
|
||||
forbiddenErrors.deviceTypes = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
fetchRoles = value => {
|
||||
const config = this.props.context;
|
||||
this.lastFetchId += 1;
|
||||
const fetchId = this.lastFetchId;
|
||||
this.setState({ data: [], fetching: true });
|
||||
|
||||
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
|
||||
return;
|
||||
}
|
||||
|
||||
const data = res.data.data.roles.map(role => ({
|
||||
text: role,
|
||||
value: role,
|
||||
}));
|
||||
|
||||
this.setState({
|
||||
unrestrictedRoles: data,
|
||||
fetching: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load roles.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
fetching: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handleRoleSearch = roleSearchValue => {
|
||||
this.setState({
|
||||
roleSearchValue,
|
||||
unrestrictedRoles: [],
|
||||
fetching: false,
|
||||
});
|
||||
};
|
||||
|
||||
// Event handler for selecting the device type
|
||||
handleSelect = event => {
|
||||
this.setState({
|
||||
selectedValue: event,
|
||||
});
|
||||
if (this.props.selectedValueHandler) {
|
||||
this.props.selectedValueHandler(event);
|
||||
}
|
||||
};
|
||||
|
||||
// Event handler for selecting the windows app type
|
||||
handleSelectForAppType = event => {
|
||||
if (this.props.selectedAppTypeHandler) {
|
||||
this.props.selectedAppTypeHandler(event);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const config = this.props.context;
|
||||
// Windows installation app types
|
||||
const appTypes = config.windowsDeviceType.appType;
|
||||
const { formConfig } = this.props;
|
||||
const {
|
||||
categories,
|
||||
tags,
|
||||
deviceTypes,
|
||||
selectedValue,
|
||||
fetching,
|
||||
unrestrictedRoles,
|
||||
} = this.state;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col md={5}></Col>
|
||||
<Col md={14}>
|
||||
<Form
|
||||
labelAlign="right"
|
||||
layout="horizontal"
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{formConfig.installationType !== 'WEB_CLIP' && (
|
||||
<div>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/admin/device-type/view"
|
||||
no={
|
||||
<Alert
|
||||
message="You don't have permission to view device types."
|
||||
type="warning"
|
||||
banner
|
||||
closable
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Form.Item {...formItemLayout} label="Device Type">
|
||||
{getFieldDecorator('deviceType', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select device type',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="select device type"
|
||||
onSelect={this.handleSelect.bind(this)}
|
||||
>
|
||||
{deviceTypes.map(deviceType => {
|
||||
return (
|
||||
<Option key={deviceType.name}>
|
||||
{deviceType.name}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* app name*/}
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/view"
|
||||
no={
|
||||
<Alert
|
||||
message="You don't have permission to compare application names. If you have update permission,
|
||||
you still can add application but app name should be unique."
|
||||
type="warning"
|
||||
banner
|
||||
closable
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Form.Item {...formItemLayout} label="App Name">
|
||||
{getFieldDecorator('name', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input a name',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="ex: Lorem App" />)}
|
||||
</Form.Item>
|
||||
|
||||
{/* App Type only shown for windows device types for enterprise apps */}
|
||||
{selectedValue === 'windows' &&
|
||||
this.props.formConfig.installationType === 'ENTERPRISE' && (
|
||||
<Form.Item {...formItemLayout} label="App Type">
|
||||
{getFieldDecorator('appType', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select app type',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="select application type"
|
||||
onSelect={this.handleSelectForAppType}
|
||||
>
|
||||
{appTypes.map(appType => {
|
||||
return <Option key={appType}>{appType}</Option>;
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* description*/}
|
||||
<Form.Item {...formItemLayout} label="Description">
|
||||
{getFieldDecorator('description', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a description',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea placeholder="Enter the description..." rows={7} />,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{/* Unrestricted Roles*/}
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/roles/view"
|
||||
no={
|
||||
<Alert
|
||||
message="You don't have permission to view roles."
|
||||
type="warning"
|
||||
banner
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Form.Item {...formItemLayout} label="Visible Roles">
|
||||
{getFieldDecorator('unrestrictedRoles', {
|
||||
rules: [],
|
||||
initialValue: [],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
labelInValue
|
||||
// value={roleSearchValue}
|
||||
placeholder="Search roles"
|
||||
notFoundContent={fetching ? <Spin size="small" /> : null}
|
||||
filterOption={false}
|
||||
onSearch={this.fetchRoles}
|
||||
onChange={this.handleRoleSearch}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{unrestrictedRoles.map(d => (
|
||||
<Option key={d.value}>{d.text}</Option>
|
||||
))}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="Categories">
|
||||
{getFieldDecorator('categories', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select categories',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Category"
|
||||
onChange={this.handleCategoryChange}
|
||||
>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<Option key={category.categoryName}>
|
||||
{category.categoryName}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="Tags">
|
||||
{getFieldDecorator('tags', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select tags',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Tags"
|
||||
>
|
||||
{tags.map(tag => {
|
||||
return <Option key={tag.tagName}>{tag.tagName}</Option>;
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{ float: 'right' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Next
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(
|
||||
Form.create({ name: 'app-details-form' })(NewAppDetailsForm),
|
||||
);
|
@ -1,926 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { MinusOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Upload,
|
||||
Typography,
|
||||
Modal,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import '@babel/polyfill';
|
||||
import Authorized from '../../../../../../../../components/Authorized/Authorized';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 8 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 16 },
|
||||
},
|
||||
};
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
const InputGroup = Input.Group;
|
||||
|
||||
function getBase64(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
// function for access the full name of the binary file using the installation path
|
||||
function extractBinaryFileName(installationPath) {
|
||||
let UploadedBinaryName = installationPath.split('/');
|
||||
return UploadedBinaryName[UploadedBinaryName.length - 1];
|
||||
}
|
||||
|
||||
class NewAppUploadForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
icons: [],
|
||||
screenshots: [],
|
||||
loading: false,
|
||||
binaryFiles: [],
|
||||
application: null,
|
||||
isFree: true,
|
||||
previewVisible: false,
|
||||
previewImage: '',
|
||||
binaryFileHelperText: '',
|
||||
iconHelperText: '',
|
||||
screenshotHelperText: '',
|
||||
osVersionsHelperText: '',
|
||||
osVersionsValidateStatus: 'validating',
|
||||
metaData: [],
|
||||
appType: null,
|
||||
};
|
||||
this.lowerOsVersion = null;
|
||||
this.upperOsVersion = null;
|
||||
}
|
||||
|
||||
normFile = e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
return e && e.fileList;
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const { formConfig } = this.props;
|
||||
const { specificElements } = formConfig;
|
||||
let windowsAppTypeMetaArray = [];
|
||||
let metaValue = [];
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
const {
|
||||
binaryFile,
|
||||
icon,
|
||||
screenshots,
|
||||
releaseDescription,
|
||||
releaseType,
|
||||
} = values;
|
||||
|
||||
/**
|
||||
* To save the metaData value that receive from
|
||||
* metaData UI In an windows app type creation
|
||||
*/
|
||||
if (
|
||||
((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows') ||
|
||||
this.props.deviceType === 'windows') &&
|
||||
this.state.metaData.length !== 0
|
||||
) {
|
||||
metaValue = [...this.state.metaData];
|
||||
}
|
||||
|
||||
// add release data
|
||||
const release = {
|
||||
description: releaseDescription,
|
||||
price: 0,
|
||||
isSharedWithAllTenants: false,
|
||||
metaData: JSON.stringify(this.state.metaData),
|
||||
releaseType: releaseType,
|
||||
};
|
||||
|
||||
const data = new FormData();
|
||||
const config = this.props.context;
|
||||
// Accessing the Meta Key value for windows device type from the config.json file
|
||||
const metaKeyValues =
|
||||
config.windowsAppxMsiKeyValueForMetaData.metaKeyArray;
|
||||
|
||||
/*
|
||||
Setting up the app type specific values to the
|
||||
metaData state field and Setting up the version
|
||||
and the packageName for windows type
|
||||
*/
|
||||
if (
|
||||
(this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows') ||
|
||||
this.props.deviceType === 'windows'
|
||||
) {
|
||||
// Setting up the version and packageName
|
||||
release.version = values.version;
|
||||
release.packageName = values.packageName;
|
||||
// setting the metaData value for appx type
|
||||
if (
|
||||
this.props.selectedAppType === 'appx' ||
|
||||
this.state.appType === 'appx'
|
||||
) {
|
||||
windowsAppTypeMetaArray = [
|
||||
{
|
||||
key: metaKeyValues[0],
|
||||
value: values.packageUrl,
|
||||
},
|
||||
{
|
||||
key: metaKeyValues[1],
|
||||
value: values.dependencyPackageUrl,
|
||||
},
|
||||
{
|
||||
key: metaKeyValues[2],
|
||||
value: values.certificateHash,
|
||||
},
|
||||
{
|
||||
key: metaKeyValues[3],
|
||||
value: values.encodedCertContent,
|
||||
},
|
||||
{
|
||||
key: metaKeyValues[4],
|
||||
value: values.packageName,
|
||||
},
|
||||
];
|
||||
windowsAppTypeMetaArray = [
|
||||
...windowsAppTypeMetaArray,
|
||||
...metaValue,
|
||||
];
|
||||
} else if (
|
||||
this.props.selectedAppType === 'msi' ||
|
||||
this.state.appType === 'msi'
|
||||
) {
|
||||
windowsAppTypeMetaArray = [
|
||||
{
|
||||
key: metaKeyValues[5],
|
||||
value: values.productId,
|
||||
},
|
||||
{
|
||||
key: metaKeyValues[6],
|
||||
value: values.contentUri,
|
||||
},
|
||||
{
|
||||
key: metaKeyValues[7],
|
||||
value: values.fileHash,
|
||||
},
|
||||
];
|
||||
windowsAppTypeMetaArray = [
|
||||
...windowsAppTypeMetaArray,
|
||||
...metaValue,
|
||||
];
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
metaData: windowsAppTypeMetaArray,
|
||||
},
|
||||
() => {
|
||||
release.metaData = JSON.stringify(this.state.metaData);
|
||||
this.props.onSuccessReleaseData({ data, release });
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty('version')) {
|
||||
release.version = values.version;
|
||||
}
|
||||
if (specificElements.hasOwnProperty('url')) {
|
||||
release.url = values.url;
|
||||
}
|
||||
if (specificElements.hasOwnProperty('packageName')) {
|
||||
release.packageName = values.packageName;
|
||||
}
|
||||
|
||||
let isFormValid = true; // flag to check if this form is valid
|
||||
|
||||
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)
|
||||
) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
osVersionsHelperText: 'Please select valid range',
|
||||
osVersionsValidateStatus: 'error',
|
||||
});
|
||||
} else {
|
||||
release.supportedOsVersions = `${this.lowerOsVersion}-${this.upperOsVersion}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
specificElements.hasOwnProperty('binaryFile') &&
|
||||
this.state.binaryFiles.length !== 1
|
||||
) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
binaryFileHelperText: 'Please select the application',
|
||||
});
|
||||
}
|
||||
if (this.state.icons.length !== 1) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
iconHelperText: 'Please select an icon',
|
||||
});
|
||||
}
|
||||
if (this.state.screenshots.length !== 3) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
screenshotHelperText: 'Please select 3 screenshots',
|
||||
});
|
||||
}
|
||||
if (this.state.screenshots.length !== 3) {
|
||||
isFormValid = false;
|
||||
this.setState({
|
||||
screenshotHelperText: 'Please select 3 screenshots',
|
||||
});
|
||||
}
|
||||
if (isFormValid) {
|
||||
data.append('icon', icon[0].originFileObj);
|
||||
data.append('screenshot1', screenshots[0].originFileObj);
|
||||
data.append('screenshot2', screenshots[1].originFileObj);
|
||||
data.append('screenshot3', screenshots[2].originFileObj);
|
||||
if (specificElements.hasOwnProperty('binaryFile')) {
|
||||
data.append('binaryFile', binaryFile[0].originFileObj);
|
||||
}
|
||||
// Condition to check is it not an Enterprise windows app creation or release
|
||||
if (
|
||||
!(
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.formConfig.installationType === 'ENTERPRISE'
|
||||
) &&
|
||||
this.props.deviceType !== 'windows'
|
||||
) {
|
||||
this.props.onSuccessReleaseData({ data, release });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleIconChange = ({ fileList }) => {
|
||||
if (fileList.length === 1) {
|
||||
this.setState({
|
||||
iconHelperText: '',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
icons: fileList,
|
||||
});
|
||||
};
|
||||
|
||||
handleBinaryFileChange = ({ fileList }) => {
|
||||
let validity = true;
|
||||
// To set the app type of windows by using the binary file in an new app release
|
||||
if (this.props.formConfig.isNewRelease && fileList.length !== 0) {
|
||||
let firstUploadedBinaryFileName = extractBinaryFileName(
|
||||
this.props.uploadedInstalltionAppType,
|
||||
);
|
||||
let FirstFileExtension = firstUploadedBinaryFileName.substr(
|
||||
firstUploadedBinaryFileName.lastIndexOf('.') + 1,
|
||||
);
|
||||
let LastFileExtension = fileList[0].name.substr(
|
||||
fileList[0].name.lastIndexOf('.') + 1,
|
||||
);
|
||||
if (FirstFileExtension !== LastFileExtension) {
|
||||
validity = false;
|
||||
} else if (LastFileExtension === 'msi' || LastFileExtension === 'appx') {
|
||||
this.setState({
|
||||
appType: LastFileExtension,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (fileList.length === 1) {
|
||||
this.setState({
|
||||
binaryFileHelperText: '',
|
||||
});
|
||||
}
|
||||
|
||||
if (validity) {
|
||||
this.setState({
|
||||
binaryFiles: fileList,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
binaryFileHelperText: 'Upload Correct Binary File extension',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleScreenshotChange = ({ fileList }) => {
|
||||
if (fileList.length === 3) {
|
||||
this.setState({
|
||||
screenshotHelperText: '',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
screenshots: fileList,
|
||||
});
|
||||
};
|
||||
|
||||
handlePreviewCancel = () => this.setState({ previewVisible: false });
|
||||
handlePreview = async file => {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = await getBase64(file.originFileObj);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
previewImage: file.url || file.preview,
|
||||
previewVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
addNewMetaData = () => {
|
||||
this.setState({
|
||||
metaData: this.state.metaData.concat({ key: '', value: '' }),
|
||||
});
|
||||
};
|
||||
|
||||
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: '',
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { formConfig, supportedOsVersions } = this.props;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const config = this.props.context;
|
||||
const {
|
||||
icons,
|
||||
screenshots,
|
||||
binaryFiles,
|
||||
previewImage,
|
||||
previewVisible,
|
||||
binaryFileHelperText,
|
||||
iconHelperText,
|
||||
screenshotHelperText,
|
||||
metaData,
|
||||
osVersionsHelperText,
|
||||
osVersionsValidateStatus,
|
||||
} = this.state;
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<PlusOutlined />
|
||||
<div className="ant-upload-text">Select</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col md={5}></Col>
|
||||
<Col md={14}>
|
||||
<Form
|
||||
labelAlign="right"
|
||||
layout="horizontal"
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{formConfig.specificElements.hasOwnProperty('binaryFile') && (
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Application"
|
||||
validateStatus="error"
|
||||
help={binaryFileHelperText}
|
||||
>
|
||||
{getFieldDecorator('binaryFile', {
|
||||
valuePropName: 'binaryFile',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select application',
|
||||
})(
|
||||
<Upload
|
||||
name="binaryFile"
|
||||
onChange={this.handleBinaryFileChange}
|
||||
beforeUpload={() => false}
|
||||
>
|
||||
{binaryFiles.length !== 1 && (
|
||||
<Button>
|
||||
<UploadOutlined /> Click to upload
|
||||
</Button>
|
||||
)}
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Icon"
|
||||
validateStatus="error"
|
||||
help={iconHelperText}
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
{getFieldDecorator('icon', {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="logo"
|
||||
listType="picture-card"
|
||||
onChange={this.handleIconChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{icons.length === 1 ? null : uploadButton}
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Row
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
<Col xs={24} sm={8}></Col>
|
||||
<Col xs={24} sm={16}>
|
||||
<Text type="secondary">Recommended : 240px x 240px</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Screenshots"
|
||||
validateStatus="error"
|
||||
help={screenshotHelperText}
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
{getFieldDecorator('screenshots', {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="screenshots"
|
||||
listType="picture-card"
|
||||
onChange={this.handleScreenshotChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{screenshots.length >= 3 ? null : uploadButton}
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<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
|
||||
recommended screenshot size - min. 320px max. 3840px.
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Package Name field for windows device type and other specific scene using it */}
|
||||
{(formConfig.specificElements.hasOwnProperty('packageName') ||
|
||||
(this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows') ||
|
||||
this.props.deviceType === 'windows') && (
|
||||
<Form.Item {...formItemLayout} label="Package Name">
|
||||
{getFieldDecorator('packageName', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the package name',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Package Name" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{formConfig.specificElements.hasOwnProperty('url') && (
|
||||
<Form.Item {...formItemLayout} label="URL">
|
||||
{getFieldDecorator('url', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the url',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="url" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* Version field for windows device type and other specific scene using it */}
|
||||
{(formConfig.specificElements.hasOwnProperty('version') ||
|
||||
(this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows') ||
|
||||
this.props.deviceType === 'windows') && (
|
||||
<Form.Item {...formItemLayout} label="Version">
|
||||
{getFieldDecorator('version', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the version',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Version" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* Windows Appx App Type Fields */}
|
||||
{/* For Windows appx app type only -> Package Url */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'appx') ||
|
||||
this.state.appType === 'appx') && (
|
||||
<Form.Item {...formItemLayout} label="Package Url">
|
||||
{getFieldDecorator('packageUrl', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the package url',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Package Url" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* For Windows appx app type only -> Dependency Package Url */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'appx') ||
|
||||
this.state.appType === 'appx') && (
|
||||
<Form.Item {...formItemLayout} label="Dependency Package Url">
|
||||
{getFieldDecorator('dependencyPackageUrl', {})(
|
||||
<Input placeholder="Dependency Package Url" />,
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* For Windows appx app type only -> Certificate Hash */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'appx') ||
|
||||
this.state.appType === 'appx') && (
|
||||
<Form.Item {...formItemLayout} label="Certificate Hash">
|
||||
{getFieldDecorator('certificateHash', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the certificate hash',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Certificate Hash" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* For Windows appx app type only -> Encoded Certificate Content */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'appx') ||
|
||||
this.state.appType === 'appx') && (
|
||||
<Form.Item {...formItemLayout} label="Encoded Cert Content">
|
||||
{getFieldDecorator('encodedCertContent', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Give the encoded cert content',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea
|
||||
placeholder="Enter a encoded certificate content"
|
||||
rows={5}
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* Windows MSI App Type Fields */}
|
||||
{/* For Windows msi app type only -> Product Id */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'msi') ||
|
||||
this.state.appType === 'msi') && (
|
||||
<Form.Item {...formItemLayout} label="Product Id">
|
||||
{getFieldDecorator('productId', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the product id',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Product Id" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* For Windows msi app type only -> Content URI */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'msi') ||
|
||||
this.state.appType === 'msi') && (
|
||||
<Form.Item {...formItemLayout} label="Content URI">
|
||||
{getFieldDecorator('contentUri', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the content uri',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Content Uri" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* For Windows msi app type only -> File Hash */}
|
||||
{((this.props.formConfig.installationType === 'ENTERPRISE' &&
|
||||
this.props.selectedValue === 'windows' &&
|
||||
this.props.selectedAppType === 'msi') ||
|
||||
this.state.appType === 'msi') && (
|
||||
<Form.Item {...formItemLayout} label="File Hash">
|
||||
{getFieldDecorator('fileHash', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the file hash',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="File Hash" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item {...formItemLayout} label="Release Type">
|
||||
{getFieldDecorator('releaseType', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the Release Type',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Release Type" />)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...formItemLayout} label="Description">
|
||||
{getFieldDecorator('releaseDescription', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a description for release',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea
|
||||
placeholder="Enter a description for release"
|
||||
rows={5}
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{formConfig.installationType !== 'WEB_CLIP' &&
|
||||
formConfig.installationType !== 'CUSTOM' && (
|
||||
<div>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/admin/device-type"
|
||||
no={
|
||||
<Alert
|
||||
message="You don't have permission to view supported OS versions."
|
||||
type="warning"
|
||||
banner
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Supported OS Versions"
|
||||
validateStatus={osVersionsValidateStatus}
|
||||
help={osVersionsHelperText}
|
||||
>
|
||||
{getFieldDecorator('supportedOS', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
initialValue: false,
|
||||
})(
|
||||
<div>
|
||||
<InputGroup>
|
||||
<Row gutter={8}>
|
||||
<Col span={11}>
|
||||
<Select
|
||||
placeholder="Lower version"
|
||||
style={{ width: '100%' }}
|
||||
onChange={this.handleLowerOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<p> - </p>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Upper version"
|
||||
defaultActiveFirstOption={true}
|
||||
onChange={this.handleUpperOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.length > 0 && (
|
||||
<Option key="all" value="all">
|
||||
All
|
||||
</Option>
|
||||
)}
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Meta Data">
|
||||
{getFieldDecorator('meta', {})(
|
||||
<div>
|
||||
{metaData.map((data, index) => {
|
||||
/*
|
||||
Exclude showing the values related to
|
||||
windows app type variables in meta Data UI
|
||||
*/
|
||||
if (
|
||||
!config.windowsAppxMsiKeyValueForMetaData.metaKeyArray.includes(
|
||||
data.key,
|
||||
)
|
||||
) {
|
||||
return (
|
||||
<InputGroup key={index}>
|
||||
<Row gutter={8}>
|
||||
<Col span={5}>
|
||||
<Input
|
||||
placeholder="key"
|
||||
value={data.key}
|
||||
onChange={e => {
|
||||
metaData[index].key = e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Input
|
||||
placeholder="value"
|
||||
value={data.value}
|
||||
onChange={e => {
|
||||
metaData[index].value =
|
||||
e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="circle"
|
||||
icon={<MinusOutlined />}
|
||||
onClick={() => {
|
||||
metaData.splice(index, 1);
|
||||
this.setState({
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<Button
|
||||
type="dashed"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={this.addNewMetaData}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<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}
|
||||
>
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(
|
||||
Form.create({ name: 'app-upload-form' })(NewAppUploadForm),
|
||||
);
|
@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import { Card, Button, Steps, Row, Col, Result, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import NewAppDetailsForm from './components/NewAppDetailsForm';
|
||||
import NewAppUploadForm from './components/NewAppUploadForm';
|
||||
import { withConfigContext } from '../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../services/utils/errorHandler';
|
||||
|
||||
const { Step } = Steps;
|
||||
|
||||
class AddNewAppFormComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: [],
|
||||
tags: [],
|
||||
icons: [],
|
||||
screenshots: [],
|
||||
loading: false,
|
||||
binaryFiles: [],
|
||||
application: null,
|
||||
release: null,
|
||||
isError: false,
|
||||
deviceType: null,
|
||||
selectedValue: null,
|
||||
selectedAppType: null,
|
||||
supportedOsVersions: [],
|
||||
errorText: '',
|
||||
forbiddenErrors: {
|
||||
supportedOsVersions: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
};
|
||||
|
||||
onSuccessReleaseData = releaseData => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true,
|
||||
isError: false,
|
||||
});
|
||||
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[formConfig.releaseWrapperName] = [release];
|
||||
|
||||
const json = JSON.stringify(application);
|
||||
const blob = new Blob([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;
|
||||
|
||||
axios
|
||||
.post(url, data)
|
||||
.then(res => {
|
||||
if (res.status === 201) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
current: 2,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
isError: true,
|
||||
current: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, error.response.data.data);
|
||||
this.setState({
|
||||
loading: false,
|
||||
isError: true,
|
||||
current: 2,
|
||||
errorText: error.response.data.data,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onClickBackButton = () => {
|
||||
const current = this.state.current - 1;
|
||||
this.setState({ current });
|
||||
};
|
||||
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
let supportedOsVersions = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
supportedOsVersions,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load supported OS versions.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// For passing the device type as the prop for other component
|
||||
selectedValueHandler = selectedValue => {
|
||||
this.setState({
|
||||
selectedValue,
|
||||
});
|
||||
};
|
||||
|
||||
// For passing the app type as the prop for other component
|
||||
selectedAppTypeHandler = selectedAppType => {
|
||||
this.setState({
|
||||
selectedAppType,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
loading,
|
||||
current,
|
||||
isError,
|
||||
supportedOsVersions,
|
||||
errorText,
|
||||
selectedValue,
|
||||
selectedAppType,
|
||||
} = 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>
|
||||
<Card style={{ marginTop: 24 }}>
|
||||
<div style={{ display: current === 0 ? 'unset' : 'none' }}>
|
||||
<NewAppDetailsForm
|
||||
formConfig={formConfig}
|
||||
selectedValueHandler={this.selectedValueHandler}
|
||||
selectedAppTypeHandler={this.selectedAppTypeHandler}
|
||||
onSuccessApplicationData={this.onSuccessApplicationData}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: current === 1 ? 'unset' : 'none' }}>
|
||||
<NewAppUploadForm
|
||||
formConfig={formConfig}
|
||||
selectedValue={selectedValue}
|
||||
selectedAppType={selectedAppType}
|
||||
supportedOsVersions={supportedOsVersions}
|
||||
onSuccessReleaseData={this.onSuccessReleaseData}
|
||||
onClickBackButton={this.onClickBackButton}
|
||||
/>
|
||||
</div>
|
||||
<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')
|
||||
}
|
||||
>
|
||||
Go to applications
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isError && (
|
||||
<Result
|
||||
status="500"
|
||||
title={errorText}
|
||||
subTitle="Go back to edit the details and submit again."
|
||||
extra={
|
||||
<Button onClick={this.onClickBackButton}>Back</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AddNewAppForm = withRouter(
|
||||
Form.create({ name: 'add-new-app' })(AddNewAppFormComponent),
|
||||
);
|
||||
export default withConfigContext(AddNewAppForm);
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Typography, Breadcrumb, Result } from 'antd';
|
||||
import AddNewAppForm from '../../components/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Authorized from '../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: 'CUSTOM',
|
||||
endpoint: '/custom-app',
|
||||
jsonPayloadName: 'application',
|
||||
releaseWrapperName: 'customAppReleaseWrappers',
|
||||
specificElements: {
|
||||
binaryFile: {
|
||||
required: true,
|
||||
},
|
||||
packageName: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewCustomApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> 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>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={<AddNewAppForm formConfig={formConfig} />}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to add new apps."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddNewCustomApp;
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Typography, Breadcrumb, Result } from 'antd';
|
||||
import AddNewAppForm from '../../components/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Authorized from '../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: 'ENTERPRISE',
|
||||
endpoint: '/ent-app',
|
||||
jsonPayloadName: 'application',
|
||||
releaseWrapperName: 'entAppReleaseWrappers',
|
||||
specificElements: {
|
||||
binaryFile: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewEnterpriseApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> 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>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={<AddNewAppForm formConfig={formConfig} />}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to add new apps."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddNewEnterpriseApp;
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Typography, Breadcrumb, Result } from 'antd';
|
||||
import AddNewAppForm from '../../components/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Authorized from '../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: 'PUBLIC',
|
||||
endpoint: '/public-app',
|
||||
jsonPayloadName: 'public-app',
|
||||
releaseWrapperName: 'publicAppReleaseWrappers',
|
||||
specificElements: {
|
||||
packageName: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewPublicApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.getCategories();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> 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>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={<AddNewAppForm formConfig={formConfig} />}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to add new apps."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddNewPublicApp;
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Typography, Breadcrumb, Result } from 'antd';
|
||||
import AddNewAppForm from '../../components/AddNewAppForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Authorized from '../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const formConfig = {
|
||||
installationType: 'WEB_CLIP',
|
||||
endpoint: '/web-app',
|
||||
jsonPayloadName: 'webapp',
|
||||
releaseWrapperName: 'webAppReleaseWrappers',
|
||||
specificElements: {
|
||||
url: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewEnterpriseApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Web Clip</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Add New Web Clip</h3>
|
||||
<Paragraph>Share a Web Clip to your corporate app store.</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={<AddNewAppForm formConfig={formConfig} />}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to add new apps."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddNewEnterpriseApp;
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.stepForm {
|
||||
max-width: 500px;
|
||||
margin: 40px auto 0;
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import { notification, Spin, Card, Row, Col } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { withConfigContext } from '../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../services/utils/errorHandler';
|
||||
import NewAppUploadForm from '../../../AddNewApp/components/AddNewAppForm/components/NewAppUploadForm';
|
||||
|
||||
const formConfig = {
|
||||
isNewRelease: true,
|
||||
specificElements: {
|
||||
binaryFile: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
class AddNewReleaseFormComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
supportedOsVersions: [],
|
||||
application: null,
|
||||
release: null,
|
||||
deviceType: null,
|
||||
forbiddenErrors: {
|
||||
supportedOsVersions: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getSupportedOsVersions(this.props.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 => {
|
||||
if (res.status === 200) {
|
||||
let supportedOsVersions = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
supportedOsVersions,
|
||||
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;
|
||||
forbiddenErrors.supportedOsVersions = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onSuccessReleaseData = releaseData => {
|
||||
const config = this.props.context;
|
||||
const { appId, deviceType } = this.props;
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
const { data, release } = releaseData;
|
||||
|
||||
const json = JSON.stringify(release);
|
||||
const blob = new Blob([json], {
|
||||
type: 'application/json',
|
||||
});
|
||||
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 => {
|
||||
if (res.status === 201) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'New release was added successfully',
|
||||
});
|
||||
const uuid = res.data.data.uuid;
|
||||
this.props.history.push({
|
||||
pathname: '/publisher/apps/releases/' + uuid,
|
||||
state: { fullAppDetails: this.props.location.state.fullAppDetails },
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Sorry, we were unable to complete your request.',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onClickBackButton = () => {
|
||||
this.props.history.push('/publisher/apps/');
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, supportedOsVersions, forbiddenErrors } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Spin tip="Uploading..." spinning={loading}>
|
||||
<Row>
|
||||
<Col span={17} offset={4}>
|
||||
<Card>
|
||||
<NewAppUploadForm
|
||||
forbiddenErrors={forbiddenErrors}
|
||||
formConfig={formConfig}
|
||||
deviceType={this.props.deviceType}
|
||||
// Takes the first upload app type installation path
|
||||
uploadedInstalltionAppType={
|
||||
this.props.location.state.appDetails.installerPath
|
||||
}
|
||||
supportedOsVersions={supportedOsVersions}
|
||||
onSuccessReleaseData={this.onSuccessReleaseData}
|
||||
onClickBackButton={this.onClickBackButton}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AddReleaseForm = withRouter(
|
||||
Form.create({ name: 'add-new-release' })(AddNewReleaseFormComponent),
|
||||
);
|
||||
export default withConfigContext(AddReleaseForm);
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Typography, Breadcrumb } from 'antd';
|
||||
import AddNewReleaseForm from './components/AddNewReleaseForm';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Paragraph = Typography;
|
||||
|
||||
class AddNewRelease extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
categories: [],
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { appId, deviceType } = this.props.match.params;
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Add New Release</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Add New Release</h3>
|
||||
<Paragraph>Add new release for the application</Paragraph>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<AddNewReleaseForm deviceType={deviceType} appId={appId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddNewRelease;
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Modal, notification, Tooltip } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { handleApiError } from '../../../../../../../../../../../services/utils/errorHandler';
|
||||
import { withConfigContext } from '../../../../../../../../../../../components/ConfigContext';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import '../../styles.css';
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
class DeleteApp extends React.Component {
|
||||
showModal = () => {
|
||||
confirm({
|
||||
title: 'Are you sure you want to delete this app?',
|
||||
icon: <ExclamationCircleOutlined style={{ color: 'red' }} />,
|
||||
content:
|
||||
'You are trying to delete the entire application, by performing this operation all ' +
|
||||
'app data, app release data, and all release artifacts will be deleted ' +
|
||||
'permanently. Further, please note, this process cannot be undone.',
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
onOk: this.deleteRelease,
|
||||
});
|
||||
};
|
||||
|
||||
deleteRelease = () => {
|
||||
const config = this.props.context;
|
||||
const apiUrl =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/' +
|
||||
this.props.id;
|
||||
axios
|
||||
.delete(apiUrl)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Successfully deleted the app',
|
||||
});
|
||||
this.props.history.push('/publisher');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Something Went wrong when trying to delete the app, Please contact the administrator',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="delete-app">
|
||||
{this.props.isDeletableApp && (
|
||||
<Button
|
||||
type="link"
|
||||
className="btn-view-more"
|
||||
onClick={this.showModal}
|
||||
>
|
||||
<DeleteOutlined /> Delete
|
||||
</Button>
|
||||
)}
|
||||
{!this.props.isDeletableApp && (
|
||||
<Tooltip
|
||||
placement="leftTop"
|
||||
title="All application releases should be in a deletable state."
|
||||
>
|
||||
<Button type="link" disabled={true} className="btn-view-more">
|
||||
<DeleteOutlined /> Delete
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(DeleteApp));
|
@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Modal, notification, Popover, Spin, Tooltip } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../../../../services/utils/errorHandler';
|
||||
import { SettingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import '../../styles.css';
|
||||
|
||||
class ManagedConfigurationsIframe extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.config = this.props.context;
|
||||
this.state = {
|
||||
visible: false,
|
||||
loading: false,
|
||||
isHintVisible: false,
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.getMcm();
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
getMcm = () => {
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
let mcmId = null;
|
||||
if (res.data.hasOwnProperty('data')) {
|
||||
mcmId = res.data.data.mcmId;
|
||||
}
|
||||
this.loadIframe(mcmId);
|
||||
this.setState({ loading: 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';
|
||||
// eslint-disable-next-line no-undef
|
||||
gapi.load('gapi.iframes', () => {
|
||||
const parameters = {
|
||||
token: this.config.androidEnterpriseToken,
|
||||
packageName: packageName,
|
||||
};
|
||||
if (mcmId != null) {
|
||||
parameters.mcmId = mcmId;
|
||||
parameters.canDelete = true;
|
||||
method = 'put';
|
||||
}
|
||||
|
||||
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' },
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
var iframe = gapi.iframes.getContext().openChild(options);
|
||||
iframe.register(
|
||||
'onconfigupdated',
|
||||
event => {
|
||||
this.updateConfig(method, event);
|
||||
},
|
||||
// eslint-disable-next-line no-undef
|
||||
gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER,
|
||||
);
|
||||
|
||||
iframe.register(
|
||||
'onconfigdeleted',
|
||||
event => {
|
||||
this.deleteConfig(event);
|
||||
},
|
||||
// eslint-disable-next-line no-undef
|
||||
gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
updateConfig = (method, event) => {
|
||||
const { packageName } = this.props;
|
||||
this.setState({ loading: true });
|
||||
|
||||
const data = {
|
||||
mcmId: event.mcmId,
|
||||
profileName: event.name,
|
||||
packageName,
|
||||
};
|
||||
|
||||
// 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 => {
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Configuration Profile updated Successfully',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update configurations.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
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 => {
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Configuration Profile removed Successfully',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to remove configurations.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
handleHintVisibleChange = visible => {
|
||||
this.setState({ isHintVisible: visible });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.props.isEnabled && (
|
||||
<Button
|
||||
type="link"
|
||||
className="btn-view-more"
|
||||
onClick={this.showModal}
|
||||
>
|
||||
<SettingOutlined /> Managed Configurations
|
||||
</Button>
|
||||
)}
|
||||
{!this.props.isEnabled && (
|
||||
<Tooltip
|
||||
placement="leftTop"
|
||||
title="Managed configurations are available only with android enterprise apps."
|
||||
>
|
||||
<Button type="link" className="btn-view-more" disabled={true}>
|
||||
<SettingOutlined /> Managed Configurations
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Modal
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={null}
|
||||
>
|
||||
<div>
|
||||
<Popover
|
||||
title={null}
|
||||
trigger="click"
|
||||
visible={this.state.isHintVisible}
|
||||
content={
|
||||
<p>
|
||||
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 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.
|
||||
</p>
|
||||
}
|
||||
onVisibleChange={this.handleHintVisibleChange}
|
||||
overlayStyle={{ width: 300 }}
|
||||
>
|
||||
<Button
|
||||
size="large"
|
||||
type="link"
|
||||
style={{ marginTop: -56, fontSize: '1.2em' }}
|
||||
>
|
||||
<QuestionCircleOutlined />
|
||||
</Button>
|
||||
</Popover>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<div id="manage-config-iframe-container"></div>
|
||||
</Spin>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ManagedConfigurationsIframe);
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EyeInvisibleOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Modal, notification, Tooltip } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { handleApiError } from '../../../../../../../../../../../services/utils/errorHandler';
|
||||
import { withConfigContext } from '../../../../../../../../../../../components/ConfigContext';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import '../../styles.css';
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
class RetireApp extends React.Component {
|
||||
showModal = () => {
|
||||
confirm({
|
||||
title: 'Are you sure you want to retire this app?',
|
||||
icon: <ExclamationCircleOutlined style={{ color: 'red' }} />,
|
||||
content:
|
||||
'You are trying to retire the entire application, by performing this operation, ' +
|
||||
'you will not see the app data or app release data on either publisher or store. ' +
|
||||
'Further, please note, this process cannot be undone.',
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
onOk: this.hideApp,
|
||||
});
|
||||
};
|
||||
|
||||
hideApp = () => {
|
||||
const config = this.props.context;
|
||||
const apiUrl =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/retire/' +
|
||||
this.props.id;
|
||||
axios
|
||||
.put(apiUrl)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Successfully hided the app',
|
||||
});
|
||||
this.props.history.push('/publisher');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
handleApiError(
|
||||
error,
|
||||
'Something Went wrong when trying to reitre the app, Please contact the administrator',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.props.isHideableApp && (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={this.showModal}
|
||||
className="btn-view-more"
|
||||
>
|
||||
<EyeInvisibleOutlined /> Retire
|
||||
</Button>
|
||||
)}
|
||||
{!this.props.isHideableApp && (
|
||||
<Tooltip
|
||||
placement="leftTop"
|
||||
title="All releases should be in retired state"
|
||||
>
|
||||
<Button type="link" disabled={true} className="btn-view-more">
|
||||
<EyeInvisibleOutlined /> Retire
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(RetireApp));
|
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.release-card{
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.release-card:hover {
|
||||
background-color: rgba(15, 188, 249,0.1);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
|
||||
.release-card{
|
||||
width: 210%;
|
||||
}
|
||||
|
||||
.app-release-cards{
|
||||
display: grid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.app-details-drawer {
|
||||
text-align: right
|
||||
}
|
||||
|
||||
.btn-view-more {
|
||||
color: rgba(0, 0, 0, 0.65) !important;
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons';
|
||||
import { Avatar, Table, Tag, Badge, Alert, Tooltip } from 'antd';
|
||||
import axios from 'axios';
|
||||
import pSBC from 'shade-blend-color';
|
||||
import './styles.css';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import AppDetailsDrawer from './AppDetailsDrawer';
|
||||
import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
|
||||
import { EntgraIcon } from 'entgra-icons-react';
|
||||
|
||||
let config = null;
|
||||
|
||||
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"
|
||||
style={{
|
||||
marginRight: 20,
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
backgroundColor: pSBC(0.5, config.theme.primaryColor),
|
||||
}}
|
||||
>
|
||||
{avatarLetter}
|
||||
</Avatar>
|
||||
);
|
||||
} else {
|
||||
const { applicationReleases } = row;
|
||||
let hasPublishedRelease = false;
|
||||
for (let i = 0; i < applicationReleases.length; i++) {
|
||||
if (applicationReleases[i].currentStatus === 'PUBLISHED') {
|
||||
hasPublishedRelease = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
avatar = hasPublishedRelease ? (
|
||||
<Badge
|
||||
title="Published"
|
||||
style={{
|
||||
backgroundColor: '#52c41a',
|
||||
borderRadius: '50%',
|
||||
color: 'white',
|
||||
}}
|
||||
count={
|
||||
<Tooltip title="Published">
|
||||
<CheckCircleOutlined
|
||||
style={{
|
||||
backgroundColor: '#52c41a',
|
||||
borderRadius: '50%',
|
||||
color: 'white',
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<Avatar
|
||||
shape="square"
|
||||
size="large"
|
||||
style={{
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
src={row.applicationReleases[0].iconPath}
|
||||
/>
|
||||
</Badge>
|
||||
) : (
|
||||
<Avatar
|
||||
shape="square"
|
||||
size="large"
|
||||
style={{
|
||||
borderRadius: '28%',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
src={row.applicationReleases[0].iconPath}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{avatar}
|
||||
<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.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;
|
||||
let color = defaultPlatformIcons.default.color;
|
||||
if (defaultPlatformIcons.hasOwnProperty(platform)) {
|
||||
icon = defaultPlatformIcons[platform].icon;
|
||||
color = defaultPlatformIcons[platform].color;
|
||||
}
|
||||
return (
|
||||
<span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
|
||||
<EntgraIcon type={icon} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
dataIndex: 'type',
|
||||
},
|
||||
{
|
||||
title: 'Subscription',
|
||||
dataIndex: 'subMethod',
|
||||
},
|
||||
];
|
||||
|
||||
class AppsTable extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
pagination: {},
|
||||
apps: [],
|
||||
filters: {},
|
||||
isDrawerVisible: false,
|
||||
selectedApp: null,
|
||||
selectedAppIndex: -1,
|
||||
loading: false,
|
||||
isForbiddenErrorVisible: false,
|
||||
};
|
||||
config = this.props.context;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { filters } = this.props;
|
||||
this.setState({
|
||||
filters,
|
||||
});
|
||||
this.fetch(filters);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
const { filters } = this.props;
|
||||
if (prevProps.filters !== this.props.filters) {
|
||||
this.setState({
|
||||
filters,
|
||||
});
|
||||
this.fetch(filters);
|
||||
}
|
||||
}
|
||||
|
||||
// handler to show app drawer
|
||||
showDrawer = (app, appIndex) => {
|
||||
this.setState({
|
||||
isDrawerVisible: true,
|
||||
selectedApp: app,
|
||||
selectedAppIndex: appIndex,
|
||||
});
|
||||
};
|
||||
|
||||
// handler to close the app drawer
|
||||
closeDrawer = () => {
|
||||
this.setState({
|
||||
isDrawerVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
const pager = { ...this.state.pagination };
|
||||
pager.current = pagination.current;
|
||||
|
||||
this.setState({
|
||||
pagination: pager,
|
||||
});
|
||||
this.fetch(this.state.filters, {
|
||||
results: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
...filters,
|
||||
});
|
||||
};
|
||||
|
||||
fetch = (filters, params = {}) => {
|
||||
this.setState({ loading: true });
|
||||
const config = this.props.context;
|
||||
|
||||
if (!params.hasOwnProperty('page')) {
|
||||
params.page = 1;
|
||||
}
|
||||
|
||||
const data = {
|
||||
offset: 10 * (params.page - 1),
|
||||
limit: 10,
|
||||
...filters,
|
||||
};
|
||||
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications',
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const data = res.data.data;
|
||||
let apps = [];
|
||||
|
||||
if (res.data.data.hasOwnProperty('applications')) {
|
||||
apps = data.applications;
|
||||
}
|
||||
const pagination = { ...this.state.pagination };
|
||||
// Read total count from server
|
||||
// pagination.total = data.totalCount;
|
||||
pagination.total = data.pagination.count;
|
||||
this.setState({
|
||||
loading: false,
|
||||
apps: apps,
|
||||
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,
|
||||
});
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
onUpdateApp = (key, value) => {
|
||||
const apps = [...this.state.apps];
|
||||
apps[this.state.selectedAppIndex][key] = value;
|
||||
this.setState({
|
||||
apps,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isDrawerVisible, loading } = this.state;
|
||||
return (
|
||||
<div>
|
||||
{this.state.isForbiddenErrorVisible && (
|
||||
<Alert
|
||||
message="You don't have permission to view apps."
|
||||
type="warning"
|
||||
banner
|
||||
closable
|
||||
/>
|
||||
)}
|
||||
<div className="apps-table">
|
||||
<Table
|
||||
rowKey={record => record.id}
|
||||
dataSource={this.state.apps}
|
||||
columns={columns}
|
||||
pagination={this.state.pagination}
|
||||
onChange={this.handleTableChange}
|
||||
rowClassName="app-row"
|
||||
loading={loading}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: event => {
|
||||
this.showDrawer(record, rowIndex);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
<AppDetailsDrawer
|
||||
visible={isDrawerVisible}
|
||||
onClose={this.closeDrawer}
|
||||
app={this.state.selectedApp}
|
||||
onUpdateApp={this.onUpdateApp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(AppsTable);
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.app-row{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.apps-table{
|
||||
display: block;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import { Card, Col, Row, Typography, Divider, Select, Button } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
|
||||
import Authorized from '../../../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Title } = Typography;
|
||||
|
||||
class FiltersForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
categories: [],
|
||||
tags: [],
|
||||
deviceTypes: [],
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((err, values) => {
|
||||
for (const [key, value] of Object.entries(values)) {
|
||||
if (value === undefined) {
|
||||
delete values[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty('deviceType') && values.deviceType === 'ALL') {
|
||||
delete values.deviceType;
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty('appType') && values.appType === 'ALL') {
|
||||
delete values.appType;
|
||||
}
|
||||
|
||||
this.props.setFilters(values);
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.getCategories();
|
||||
this.getTags();
|
||||
this.getDeviceTypes();
|
||||
}
|
||||
|
||||
getCategories = () => {
|
||||
const config = this.props.context;
|
||||
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,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
let tags = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
tags: tags,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load tags.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
const deviceTypes = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
deviceTypes,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load device types.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { categories, tags, deviceTypes } = this.state;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
layout="horizontal"
|
||||
hideRequiredMark
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Title level={4}>Filter</Title>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
style={{
|
||||
float: 'right',
|
||||
marginBottom: 0,
|
||||
marginTop: -5,
|
||||
}}
|
||||
>
|
||||
<Button size="small" type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={
|
||||
<div>
|
||||
<p>Categories:</p>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('categories', {
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: 'Please select categories',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Category"
|
||||
onChange={this.handleCategoryChange}
|
||||
>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<Option key={category.categoryName}>
|
||||
{category.categoryName}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<p>Tags:</p>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('tags', {
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: 'Please select tags',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select tags"
|
||||
>
|
||||
{tags.map(tag => {
|
||||
return <Option key={tag.tagName}>{tag.tagName}</Option>;
|
||||
})}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<p>Device Type:</p>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/admin/device-type/view"
|
||||
yes={
|
||||
<Form.Item>
|
||||
{getFieldDecorator('deviceType', {
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: 'Please select device types',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select device types"
|
||||
>
|
||||
{deviceTypes.map(deviceType => {
|
||||
return (
|
||||
<Option key={deviceType.name}>{deviceType.name}</Option>
|
||||
);
|
||||
})}
|
||||
<Option key="ALL">All</Option>
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
}
|
||||
/>
|
||||
<p>App Type:</p>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('appType', {})(
|
||||
<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>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Filters = withConfigContext(
|
||||
Form.create({ name: 'filter-apps' })(FiltersForm),
|
||||
);
|
||||
|
||||
export default withConfigContext(Filters);
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Card, Col, Row, Typography, Input, Divider } from 'antd';
|
||||
import AppsTable from './components/ApssTable';
|
||||
import Filters from './components/Filters';
|
||||
|
||||
const { Title } = Typography;
|
||||
const Search = Input.Search;
|
||||
|
||||
class Apps extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filters: {},
|
||||
};
|
||||
this.appName = '';
|
||||
}
|
||||
|
||||
setFilters = filters => {
|
||||
if (this.appName === '' && filters.hasOwnProperty('appName')) {
|
||||
delete filters.appName;
|
||||
} else {
|
||||
filters.appName = this.appName;
|
||||
}
|
||||
this.setState({
|
||||
filters,
|
||||
});
|
||||
};
|
||||
|
||||
setSearchText = appName => {
|
||||
const filters = { ...this.state.filters };
|
||||
this.appName = appName;
|
||||
if (appName === '' && filters.hasOwnProperty('appName')) {
|
||||
delete filters.appName;
|
||||
} else {
|
||||
filters.appName = appName;
|
||||
}
|
||||
this.setState({
|
||||
filters,
|
||||
});
|
||||
};
|
||||
|
||||
onChangeSearchText = e => {
|
||||
const filters = { ...this.state.filters };
|
||||
const appName = e.target.value;
|
||||
if (appName === '' && filters.hasOwnProperty('appName')) {
|
||||
delete filters.appName;
|
||||
this.setState({
|
||||
filters,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { filters } = this.state;
|
||||
return (
|
||||
<Card>
|
||||
<Row gutter={28}>
|
||||
<Col md={6}>
|
||||
<Filters setFilters={this.setFilters} />
|
||||
</Col>
|
||||
<Col md={18}>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<Title level={4}>Apps</Title>
|
||||
</Col>
|
||||
<Col span={18} style={{ textAlign: 'right' }}>
|
||||
<Search
|
||||
placeholder="Search by app name"
|
||||
onSearch={this.setSearchText}
|
||||
onChange={this.onChangeSearchText}
|
||||
style={{ width: 240, zIndex: 0 }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider dashed={true} />
|
||||
<AppsTable filters={filters} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Apps;
|
@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { TeamOutlined } from '@ant-design/icons';
|
||||
import { Row, Typography } from 'antd';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
import './styles.css';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../services/utils/errorHandler';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
class DetailedRating extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
detailedRating: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
let detailedRating = res.data.data;
|
||||
this.setState({
|
||||
detailedRating,
|
||||
});
|
||||
}
|
||||
})
|
||||
.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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const totalCount = detailedRating.noOfUsers;
|
||||
const ratingVariety = detailedRating.ratingVariety;
|
||||
|
||||
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];
|
||||
|
||||
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">
|
||||
<div className="rate">{detailedRating.ratingValue.toFixed(1)}</div>
|
||||
<StarRatings
|
||||
rating={detailedRating.ratingValue}
|
||||
starRatedColor="#777"
|
||||
starDimension="16px"
|
||||
starSpacing="2px"
|
||||
numberOfStars={5}
|
||||
name="rating"
|
||||
/>
|
||||
<br />
|
||||
<Text type="secondary" className="people-count">
|
||||
<TeamOutlined /> {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>
|
||||
</div>
|
||||
<div className="bar-container">
|
||||
<span className="number">4</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>
|
||||
</div>
|
||||
<div className="bar-container">
|
||||
<span className="number">2</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>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(DetailedRating);
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.d-rating .numeric-data{
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 20px 0 20px 0;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.d-rating .bar-containers{
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 20px 20px 20px 30px;
|
||||
vertical-align: top;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
|
||||
.d-rating .bar-containers{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.d-rating .numeric-data{
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.d-rating .bar-containers .bar-container{
|
||||
color: #737373;
|
||||
font-weight: 400;
|
||||
height: 20px;
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.d-rating .bar-containers .bar-container .number{
|
||||
font-size: 11px;
|
||||
left: -16px;
|
||||
letter-spacing: 1px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.d-rating .bar-containers .bar-container .bar{
|
||||
transition: width .25s ease;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
opacity: .8;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.bar-container .rate-5{
|
||||
background: #57bb8a;
|
||||
}
|
||||
|
||||
.bar-container .rate-4{
|
||||
background: #9ace6a;
|
||||
}
|
||||
|
||||
.bar-container .rate-3{
|
||||
background: #ffcf02;
|
||||
}
|
||||
|
||||
.bar-container .rate-2{
|
||||
background: #ff9f02;
|
||||
}
|
||||
|
||||
.bar-container .rate-1{
|
||||
background: #ff6f31;
|
||||
}
|
||||
|
||||
.d-rating .numeric-data .rate{
|
||||
color: #333;
|
||||
font-size: 64px;
|
||||
font-weight: 100;
|
||||
line-height: 64px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.d-rating .numeric-data .people-count{
|
||||
padding-top: 6px;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import AppList from './components/AppList';
|
||||
import Authorized from '../../../../components/Authorized/Authorized';
|
||||
import { Result } from 'antd';
|
||||
|
||||
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 }}>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/view"
|
||||
yes={<AppList />}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to view apps."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Apps;
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Tag, Timeline, Card } from 'antd';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
|
||||
class LifeCycleHistory extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lifeCycleStates } = this.props;
|
||||
return (
|
||||
<div className="scroll" style={{ height: 500, overflowY: 'auto' }}>
|
||||
<Timeline mode={'alternate'} style={{ marginTop: 10 }}>
|
||||
{lifeCycleStates.map(
|
||||
(state, index) =>
|
||||
state && (
|
||||
<Timeline.Item key={index} label={state.updatedAt}>
|
||||
<Card>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
{state.previousState === state.currentState ? (
|
||||
'Application Created'
|
||||
) : (
|
||||
<div>
|
||||
State changed from <br />
|
||||
<div style={{ marginTop: 5 }}>
|
||||
<Tag color="blue">{state.previousState}</Tag> to{' '}
|
||||
<Tag color="blue">{state.currentState}</Tag>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Tag style={{ marginTop: 5 }}>{state.updatedAt}</Tag>
|
||||
</div>
|
||||
</Card>
|
||||
</Timeline.Item>
|
||||
),
|
||||
)}
|
||||
</Timeline>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(LifeCycleHistory);
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { Modal, Button, Tag, List, Typography } from 'antd';
|
||||
import pSBC from 'shade-blend-color';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
class LifeCycleDetailsModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { visible: false };
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const config = this.props.context;
|
||||
const lifeCycleConfig = config.lifecycle;
|
||||
const { lifecycle } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<QuestionCircleOutlined />}
|
||||
onClick={this.showModal}
|
||||
>
|
||||
Learn more
|
||||
</Button>
|
||||
<Modal
|
||||
title="Lifecycle"
|
||||
visible={this.state.visible}
|
||||
footer={null}
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={Object.keys(lifecycle)}
|
||||
renderItem={lifecycleState => {
|
||||
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:';
|
||||
}
|
||||
|
||||
return (
|
||||
<List.Item>
|
||||
<List.Item.Meta title={lifecycleState} />
|
||||
{text}
|
||||
<br />
|
||||
<Text type="secondary">{footerText}</Text>
|
||||
<div>
|
||||
{nextProceedingStates.map(lifecycleState => {
|
||||
return (
|
||||
<Tag
|
||||
key={lifecycleState}
|
||||
style={{ margin: 5 }}
|
||||
color={pSBC(0.3, config.theme.primaryColor)}
|
||||
>
|
||||
{lifecycleState}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</List.Item>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(LifeCycleDetailsModal);
|
@ -1,354 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Typography,
|
||||
Tag,
|
||||
Divider,
|
||||
Button,
|
||||
Modal,
|
||||
notification,
|
||||
Steps,
|
||||
Alert,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import ReactQuill from 'react-quill';
|
||||
import 'react-quill/dist/quill.snow.css';
|
||||
import './styles.css';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
|
||||
import LifeCycleHistory from './components/LifeCycleHistory';
|
||||
import { EntgraIcon } from 'entgra-icons-react';
|
||||
const { Text, Title, Paragraph } = Typography;
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const modules = {
|
||||
toolbar: [
|
||||
[{ header: [1, 2, false] }],
|
||||
['bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block'],
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
['link', 'image'],
|
||||
],
|
||||
};
|
||||
|
||||
const formats = [
|
||||
'header',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strike',
|
||||
'blockquote',
|
||||
'code-block',
|
||||
'list',
|
||||
'bullet',
|
||||
'link',
|
||||
'image',
|
||||
];
|
||||
|
||||
const { Step } = Steps;
|
||||
|
||||
class LifeCycle extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
currentStatus: props.currentStatus,
|
||||
selectedStatus: null,
|
||||
reasonText: '',
|
||||
isReasonModalVisible: false,
|
||||
isConfirmButtonLoading: false,
|
||||
current: 0,
|
||||
lifecycleSteps: [],
|
||||
lifeCycleStates: [],
|
||||
isPublished: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const config = this.props.context;
|
||||
const lifeCycleConfig = config.lifecycle;
|
||||
const lifecycleSteps = Object.keys(lifeCycleConfig).map(config => {
|
||||
return lifeCycleConfig[config];
|
||||
});
|
||||
let isPublished = this.checkReleaseLifeCycleStatus();
|
||||
this.setState({
|
||||
current: lifeCycleConfig[this.props.currentStatus].step,
|
||||
lifecycleSteps,
|
||||
isPublished,
|
||||
});
|
||||
this.getLifeCycleHistory();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (
|
||||
prevProps.currentStatus !== this.props.currentStatus ||
|
||||
prevProps.uuid !== this.props.uuid
|
||||
) {
|
||||
this.setState({
|
||||
currentStatus: this.props.currentStatus,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = value => {
|
||||
this.setState({ reasonText: value });
|
||||
};
|
||||
|
||||
showReasonModal = lifecycleState => {
|
||||
this.setState({
|
||||
selectedStatus: lifecycleState,
|
||||
isReasonModalVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
closeReasonModal = () => {
|
||||
this.setState({
|
||||
isReasonModalVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
addLifeCycle = () => {
|
||||
const config = this.props.context;
|
||||
const lifeCycleConfig = config.lifecycle;
|
||||
const { selectedStatus, reasonText } = this.state;
|
||||
const { uuid } = this.props;
|
||||
const data = {
|
||||
action: selectedStatus,
|
||||
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 => {
|
||||
if (res.status === 201) {
|
||||
this.setState({
|
||||
current: lifeCycleConfig[selectedStatus].step,
|
||||
isReasonModalVisible: false,
|
||||
isConfirmButtonLoading: false,
|
||||
currentStatus: selectedStatus,
|
||||
selectedStatus: null,
|
||||
reasonText: '',
|
||||
});
|
||||
this.props.changeCurrentLifecycleStatus(selectedStatus);
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Lifecycle state updated successfully!',
|
||||
});
|
||||
this.getLifeCycleHistory();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to add lifecycle');
|
||||
this.setState({
|
||||
isConfirmButtonLoading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
getLifeCycleHistory = () => {
|
||||
const config = this.props.context;
|
||||
const { uuid } = this.props;
|
||||
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/life-cycle/state-changes/' +
|
||||
uuid,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
this.setState({ lifeCycleStates: JSON.parse(res.data.data) });
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to get lifecycle history',
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
onChange = current => {
|
||||
this.setState({ current });
|
||||
};
|
||||
|
||||
/*
|
||||
Function to check if the same app releases are in published
|
||||
state or not and assigned a boolean value to disable
|
||||
the publish button if an app release is already published
|
||||
*/
|
||||
checkReleaseLifeCycleStatus = () => {
|
||||
if (typeof this.props.appReleases !== 'undefined') {
|
||||
let appReleases = this.props.appReleases.fullAppDetails;
|
||||
for (let i = 0; i < appReleases.length; i++) {
|
||||
if (
|
||||
this.props.uuid !== appReleases[i].uuid &&
|
||||
appReleases[i].currentStatus === 'PUBLISHED'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
currentStatus,
|
||||
selectedStatus,
|
||||
current,
|
||||
lifecycleSteps,
|
||||
lifeCycleStates,
|
||||
} = this.state;
|
||||
const { lifecycle } = this.props;
|
||||
const text = <span>Already an app is in publish state</span>;
|
||||
let proceedingStates = [];
|
||||
if (
|
||||
lifecycle !== null &&
|
||||
lifecycle.hasOwnProperty(currentStatus) &&
|
||||
lifecycle[currentStatus].hasOwnProperty('proceedingStates')
|
||||
) {
|
||||
proceedingStates = lifecycle[currentStatus].proceedingStates;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Title level={4}>Manage Lifecycle</Title>
|
||||
<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 />
|
||||
</Paragraph>
|
||||
<Tabs defaultActiveKey="1" type="card">
|
||||
<TabPane tab="Change Lifecycle" key="1">
|
||||
<div>
|
||||
<Steps
|
||||
direction={'vertical'}
|
||||
current={current}
|
||||
onChange={this.onChange}
|
||||
size="small"
|
||||
>
|
||||
{lifecycleSteps.map((step, index) => {
|
||||
return (
|
||||
<Step
|
||||
key={index}
|
||||
icon={<EntgraIcon type={step.icon} />}
|
||||
title={step.title}
|
||||
disabled={current !== step.step}
|
||||
description={
|
||||
current === step.step && (
|
||||
<div style={{ width: 400 }}>
|
||||
<p>{step.text}</p>
|
||||
{proceedingStates.map(lifecycleState => {
|
||||
return (
|
||||
<Tooltip
|
||||
key={lifecycleState}
|
||||
title={
|
||||
lifecycleState === 'PUBLISHED' &&
|
||||
this.state.isPublished
|
||||
? text
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size={'small'}
|
||||
style={{ marginRight: 3 }}
|
||||
disabled={
|
||||
lifecycleState === 'PUBLISHED' &&
|
||||
this.state.isPublished
|
||||
}
|
||||
onClick={() =>
|
||||
this.showReasonModal(lifecycleState)
|
||||
}
|
||||
key={lifecycleState}
|
||||
type={'primary'}
|
||||
>
|
||||
{lifecycleState}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Steps>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="Lifecycle History" key="2">
|
||||
<LifeCycleHistory lifeCycleStates={lifeCycleStates} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Divider />
|
||||
<Modal
|
||||
title="Confirm changing lifecycle state"
|
||||
visible={this.state.isReasonModalVisible}
|
||||
onOk={this.addLifeCycle}
|
||||
onCancel={this.closeReasonModal}
|
||||
okText="Confirm"
|
||||
>
|
||||
<Text>
|
||||
You are going to change the lifecycle state from,
|
||||
<br />
|
||||
<Tag color="blue">{currentStatus}</Tag>to{' '}
|
||||
<Tag color="blue">{selectedStatus}</Tag>
|
||||
</Text>
|
||||
<br />
|
||||
{lifecycle && selectedStatus && lifecycle[selectedStatus].isEndState && (
|
||||
<Alert
|
||||
message="In this state application becomes completely obsolete. Be careful,
|
||||
this process cannot be undone."
|
||||
banner
|
||||
style={{ marginTop: 5 }}
|
||||
/>
|
||||
)}
|
||||
<Divider orientation="left">Reason</Divider>
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={this.state.reasonText}
|
||||
onChange={this.handleChange}
|
||||
modules={modules}
|
||||
formats={formats}
|
||||
placeholder="Leave a comment (optional)"
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(LifeCycle);
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.ql-editor{
|
||||
min-height: 100px !important;
|
||||
max-height: 400px;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.ql-toolbar.ql-snow{
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
.ql-toolbar.ql-snow + .ql-container.ql-snow{
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
.ql-editor.ql-blank::before {
|
||||
font-style: unset;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { Modal, Button, notification } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { handleApiError } from '../../../../../../../../../../services/utils/errorHandler';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
const { confirm } = Modal;
|
||||
|
||||
class DeleteRelease extends React.Component {
|
||||
showModal = () => {
|
||||
confirm({
|
||||
title: 'Are you sure you want to delete the application release?',
|
||||
content:
|
||||
'If you have multiple application releases, only the selected app release will be ' +
|
||||
'deleted. Otherwise, the whole application will be deleted. Further note, this will ' +
|
||||
'delete application artifacts permanently.\n' +
|
||||
'Be careful, this process cannot be undone.',
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
onOk: this.deleteRelease,
|
||||
});
|
||||
};
|
||||
|
||||
deleteRelease = () => {
|
||||
const config = this.props.context;
|
||||
const apiUrl =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/admin/applications/release/' +
|
||||
this.props.uuid;
|
||||
axios
|
||||
.delete(apiUrl)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Successfully deleted the release',
|
||||
});
|
||||
this.props.history.push('/publisher');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
'Something Went wrong when trying to delete the release, Please contact the administrator',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
disabled={!this.props.isDeletableState}
|
||||
size="small"
|
||||
type="danger"
|
||||
onClick={this.showModal}
|
||||
>
|
||||
<DeleteOutlined /> Delete
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(DeleteRelease));
|
@ -1,815 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EditOutlined,
|
||||
MinusOutlined,
|
||||
PlusOutlined,
|
||||
UploadOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
notification,
|
||||
Spin,
|
||||
Tooltip,
|
||||
Upload,
|
||||
Input,
|
||||
Divider,
|
||||
Row,
|
||||
Col,
|
||||
Select,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import '@babel/polyfill';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
import Authorized from '../../../../../../../../../../components/Authorized/Authorized';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const InputGroup = Input.Group;
|
||||
const { Option } = Select;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
span: 8,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 16,
|
||||
},
|
||||
};
|
||||
|
||||
function getBase64(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
}
|
||||
// function for access the name of the binary file using the installation path
|
||||
function extractBinaryFileName(installationPath) {
|
||||
let UploadedBinaryName = installationPath.split('/');
|
||||
let binaryFileName = UploadedBinaryName[UploadedBinaryName.length - 1];
|
||||
return binaryFileName.substr(binaryFileName.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
class EditReleaseModal extends React.Component {
|
||||
// To add subscription type & tenancy sharing, refer https://gitlab.com/entgra/carbon-device-mgt/merge_requests/331
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: false,
|
||||
current: 0,
|
||||
categories: [],
|
||||
tags: [],
|
||||
icons: [],
|
||||
screenshots: [],
|
||||
loading: false,
|
||||
binaryFiles: [],
|
||||
metaData: [],
|
||||
formConfig: {
|
||||
specificElements: {},
|
||||
},
|
||||
};
|
||||
this.lowerOsVersion = null;
|
||||
this.upperOsVersion = null;
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
this.generateConfig();
|
||||
};
|
||||
|
||||
generateConfig = () => {
|
||||
const { type } = this.props;
|
||||
const formConfig = {
|
||||
type,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'ENTERPRISE':
|
||||
formConfig.endpoint = '/ent-app-release';
|
||||
formConfig.specificElements = {
|
||||
binaryFile: {
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'PUBLIC':
|
||||
formConfig.endpoint = '/public-app-release';
|
||||
formConfig.specificElements = {
|
||||
packageName: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'WEB_CLIP':
|
||||
formConfig.endpoint = '/web-app-release';
|
||||
formConfig.specificElements = {
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
url: {
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'CUSTOM':
|
||||
formConfig.endpoint = '/custom-app-release';
|
||||
formConfig.specificElements = {
|
||||
binaryFile: {
|
||||
required: true,
|
||||
},
|
||||
packageName: {
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
formConfig,
|
||||
});
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
const config = this.props.context;
|
||||
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,
|
||||
},
|
||||
releaseDescription: {
|
||||
value: release.description,
|
||||
},
|
||||
});
|
||||
|
||||
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],
|
||||
},
|
||||
upperOsVersion: {
|
||||
value: osVersions[1],
|
||||
},
|
||||
});
|
||||
}
|
||||
// Showing the packageName value in the edit form UI
|
||||
if (
|
||||
formConfig.specificElements.hasOwnProperty('packageName') ||
|
||||
(this.props.type === 'ENTERPRISE' && this.props.deviceType === 'windows')
|
||||
) {
|
||||
this.props.form.setFields({
|
||||
packageName: {
|
||||
value: release.packageName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Showing the version value in the edit form UI
|
||||
if (
|
||||
formConfig.specificElements.hasOwnProperty('version') ||
|
||||
(this.props.type === 'ENTERPRISE' && this.props.deviceType === 'windows')
|
||||
) {
|
||||
this.props.form.setFields({
|
||||
version: {
|
||||
value: release.version,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty('url')) {
|
||||
this.props.form.setFields({
|
||||
url: {
|
||||
value: release.url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
visible: true,
|
||||
metaData,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
normFile = e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
return e && e.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 config = this.props.context;
|
||||
|
||||
const { formConfig } = this.state;
|
||||
const { specificElements } = formConfig;
|
||||
// Accessing the extension type of the current uploaded binary file
|
||||
const appTypeExtension = extractBinaryFileName(
|
||||
this.props.release.installerPath,
|
||||
);
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
const { releaseDescription, releaseType } = values;
|
||||
|
||||
const { icons, screenshots, binaryFiles } = this.state;
|
||||
|
||||
const data = new FormData();
|
||||
|
||||
// add release data
|
||||
const release = {
|
||||
description: releaseDescription,
|
||||
price: 0,
|
||||
isSharedWithAllTenants: false,
|
||||
metaData: JSON.stringify(this.state.metaData),
|
||||
releaseType: releaseType,
|
||||
};
|
||||
|
||||
if (config.deviceTypes.mobileTypes.includes(this.props.deviceType)) {
|
||||
release.supportedOsVersions = `${this.lowerOsVersion}-${this.upperOsVersion}`;
|
||||
}
|
||||
|
||||
if (
|
||||
specificElements.hasOwnProperty('binaryFile') &&
|
||||
binaryFiles.length === 1
|
||||
) {
|
||||
data.append('binaryFile', binaryFiles[0].originFileObj);
|
||||
}
|
||||
|
||||
if (
|
||||
specificElements.hasOwnProperty('version') ||
|
||||
(this.props.type === 'ENTERPRISE' &&
|
||||
this.props.deviceType === 'windows')
|
||||
) {
|
||||
release.version = values.version;
|
||||
}
|
||||
|
||||
// Accessing the Meta Key value for windows device type from the config.json file
|
||||
const metaKeyValues =
|
||||
config.windowsAppxMsiKeyValueForMetaData.metaKeyArray;
|
||||
|
||||
if (
|
||||
specificElements.hasOwnProperty('packageName') ||
|
||||
(this.props.type === 'ENTERPRISE' &&
|
||||
this.props.deviceType === 'windows')
|
||||
) {
|
||||
release.packageName = values.packageName;
|
||||
// Setting up the packageName to the package_Family_Name key in an appx app type instance
|
||||
if (appTypeExtension === config.windowsDeviceType.appType[1]) {
|
||||
let metaDataArray = this.state.metaData;
|
||||
let filterMetaArray = metaDataArray.filter(
|
||||
obj => obj.key !== metaKeyValues[4],
|
||||
);
|
||||
filterMetaArray.push({
|
||||
key: metaKeyValues[4],
|
||||
value: values.packageName,
|
||||
});
|
||||
release.metaData = JSON.stringify(filterMetaArray);
|
||||
}
|
||||
}
|
||||
|
||||
if (specificElements.hasOwnProperty('url')) {
|
||||
release.url = values.url;
|
||||
}
|
||||
|
||||
if (icons.length === 1) {
|
||||
data.append('icon', icons[0].originFileObj);
|
||||
}
|
||||
|
||||
if (screenshots.length > 0) {
|
||||
data.append('screenshot1', screenshots[0].originFileObj);
|
||||
}
|
||||
|
||||
if (screenshots.length > 1) {
|
||||
data.append('screenshot2', screenshots[1].originFileObj);
|
||||
}
|
||||
|
||||
if (screenshots.length > 2) {
|
||||
data.append('screenshot3', screenshots[2].originFileObj);
|
||||
}
|
||||
|
||||
const json = JSON.stringify(release);
|
||||
const blob = new Blob([json], {
|
||||
type: 'application/json',
|
||||
});
|
||||
|
||||
data.append('applicationRelease', blob);
|
||||
|
||||
const url =
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications' +
|
||||
formConfig.endpoint +
|
||||
'/' +
|
||||
uuid;
|
||||
|
||||
axios
|
||||
.put(url, data)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
const updatedRelease = res.data.data;
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Saved!',
|
||||
});
|
||||
this.props.updateRelease(updatedRelease);
|
||||
}
|
||||
})
|
||||
.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.',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
addNewMetaData = () => {
|
||||
this.setState({
|
||||
metaData: this.state.metaData.concat({ key: '', value: '' }),
|
||||
});
|
||||
};
|
||||
|
||||
handlePreviewCancel = () => this.setState({ previewVisible: false });
|
||||
|
||||
handlePreview = async file => {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = await getBase64(file.originFileObj);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
previewImage: file.url || file.preview,
|
||||
previewVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleLowerOsVersionChange = lowerOsVersion => {
|
||||
this.lowerOsVersion = lowerOsVersion;
|
||||
};
|
||||
|
||||
handleUpperOsVersionChange = upperOsVersion => {
|
||||
this.upperOsVersion = upperOsVersion;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
formConfig,
|
||||
icons,
|
||||
screenshots,
|
||||
loading,
|
||||
binaryFiles,
|
||||
metaData,
|
||||
previewImage,
|
||||
previewVisible,
|
||||
} = this.state;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const { isAppUpdatable, supportedOsVersions, deviceType } = this.props;
|
||||
const config = this.props.context;
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<PlusOutlined />
|
||||
<div className="ant-upload-text">Select</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
title={
|
||||
isAppUpdatable
|
||||
? 'Edit this release'
|
||||
: "This release isn't in an editable state"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={!isAppUpdatable}
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={this.showModal}
|
||||
>
|
||||
<EditOutlined /> Edit
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Modal
|
||||
title="Edit release"
|
||||
visible={this.state.visible}
|
||||
footer={null}
|
||||
width={580}
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<div>
|
||||
<Spin tip="Uploading..." spinning={loading}>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
layout="horizontal"
|
||||
hideRequiredMark
|
||||
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',
|
||||
})(
|
||||
<Upload
|
||||
name="binaryFile"
|
||||
onChange={this.handleBinaryFileChange}
|
||||
beforeUpload={() => false}
|
||||
>
|
||||
{binaryFiles.length !== 1 && (
|
||||
<Button>
|
||||
<UploadOutlined /> Change
|
||||
</Button>
|
||||
)}
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
{formConfig.specificElements.hasOwnProperty('url') && (
|
||||
<Form.Item {...formItemLayout} label="URL">
|
||||
{getFieldDecorator('url', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the url',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="url" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item {...formItemLayout} label="Icon">
|
||||
{getFieldDecorator('icon', {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="logo"
|
||||
listType="picture-card"
|
||||
onChange={this.handleIconChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{icons.length === 1 ? null : uploadButton}
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...formItemLayout} label="Screenshots">
|
||||
{getFieldDecorator('screenshots', {
|
||||
valuePropName: 'icon',
|
||||
getValueFromEvent: this.normFile,
|
||||
required: true,
|
||||
message: 'Please select a icon',
|
||||
})(
|
||||
<Upload
|
||||
name="screenshots"
|
||||
listType="picture-card"
|
||||
onChange={this.handleScreenshotChange}
|
||||
beforeUpload={() => false}
|
||||
onPreview={this.handlePreview}
|
||||
>
|
||||
{screenshots.length >= 3 ? null : uploadButton}
|
||||
</Upload>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{/* Package Name field for windows device type and other specific scene using it */}
|
||||
{(formConfig.specificElements.hasOwnProperty('packageName') ||
|
||||
(this.props.type === 'ENTERPRISE' &&
|
||||
this.props.deviceType === 'windows')) && (
|
||||
<Form.Item {...formItemLayout} label="Package Name">
|
||||
{getFieldDecorator('packageName', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the package name',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Package Name" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* Version field for windows device type and other specific scene using it */}
|
||||
{(formConfig.specificElements.hasOwnProperty('version') ||
|
||||
(this.props.type === 'ENTERPRISE' &&
|
||||
this.props.deviceType === 'windows')) && (
|
||||
<Form.Item {...formItemLayout} label="Version">
|
||||
{getFieldDecorator('version', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the version',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Version" />)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item {...formItemLayout} label="Release Type">
|
||||
{getFieldDecorator('releaseType', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input the Release Type',
|
||||
},
|
||||
],
|
||||
})(<Input placeholder="Release Type" />)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...formItemLayout} label="Description">
|
||||
{getFieldDecorator('releaseDescription', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter a description for release',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<TextArea
|
||||
placeholder="Enter a description for release"
|
||||
rows={5}
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
{config.deviceTypes.mobileTypes.includes(deviceType) && (
|
||||
<div>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/admin/device-type"
|
||||
no={
|
||||
<Alert
|
||||
message="You don't have permission to view supported OS versions."
|
||||
type="warning"
|
||||
banner
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Supported OS Versions"
|
||||
>
|
||||
{getFieldDecorator('supportedOS')(
|
||||
<div>
|
||||
<InputGroup>
|
||||
<Row gutter={8}>
|
||||
<Col span={11}>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('lowerOsVersion', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select Value',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
placeholder="Lower version"
|
||||
style={{ width: '100%' }}
|
||||
onChange={this.handleLowerOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<p> - </p>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('upperOsVersion', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select Value',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Upper version"
|
||||
onChange={this.handleUpperOsVersionChange}
|
||||
>
|
||||
{supportedOsVersions.map(version => (
|
||||
<Option
|
||||
key={version.versionName}
|
||||
value={version.versionName}
|
||||
>
|
||||
{version.versionName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
<Form.Item {...formItemLayout} label="Meta Data">
|
||||
{getFieldDecorator('meta', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please fill empty fields',
|
||||
},
|
||||
],
|
||||
initialValue: false,
|
||||
})(
|
||||
<div>
|
||||
{metaData.map((data, index) => {
|
||||
if (
|
||||
!(
|
||||
data.key ===
|
||||
config.windowsAppxMsiKeyValueForMetaData
|
||||
.metaKeyArray[4]
|
||||
)
|
||||
) {
|
||||
return (
|
||||
<InputGroup key={index}>
|
||||
<Row gutter={8}>
|
||||
<Col span={10}>
|
||||
<Input
|
||||
placeholder="key"
|
||||
value={data.key}
|
||||
onChange={e => {
|
||||
metaData[index].key =
|
||||
e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Input
|
||||
placeholder="value"
|
||||
value={data.value}
|
||||
onChange={e => {
|
||||
metaData[index].value =
|
||||
e.currentTarget.value;
|
||||
this.setState({
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="circle"
|
||||
icon={<MinusOutlined />}
|
||||
onClick={() => {
|
||||
metaData.splice(index, 1);
|
||||
this.setState({
|
||||
metaData,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<Button
|
||||
type="dashed"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={this.addNewMetaData}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item style={{ float: 'right', marginLeft: 8 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Update
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item style={{ float: 'right' }}>
|
||||
<Button htmlType="button" onClick={this.handleCancel}>
|
||||
Back
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<br />
|
||||
</Form>
|
||||
</Spin>
|
||||
</div>
|
||||
<Modal
|
||||
visible={previewVisible}
|
||||
footer={null}
|
||||
onCancel={this.handlePreviewCancel}
|
||||
>
|
||||
<img
|
||||
alt="Preview Image"
|
||||
style={{ width: '100%' }}
|
||||
src={previewImage}
|
||||
/>
|
||||
</Modal>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const EditRelease = withConfigContext(
|
||||
Form.create({ name: 'add-new-release' })(EditReleaseModal),
|
||||
);
|
||||
|
||||
export default EditRelease;
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { 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',
|
||||
];
|
||||
|
||||
class SingleReview extends React.Component {
|
||||
render() {
|
||||
const review = this.props.review;
|
||||
const randomColor = colorList[Math.floor(Math.random() * colorList.length)];
|
||||
const avatarLetter = review.username.charAt(0).toUpperCase();
|
||||
const content = (
|
||||
<div style={{ marginTop: -5 }}>
|
||||
<StarRatings
|
||||
rating={review.rating}
|
||||
starRatedColor="#777"
|
||||
starDimension="12px"
|
||||
starSpacing="2px"
|
||||
numberOfStars={5}
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar
|
||||
style={{ backgroundColor: randomColor, verticalAlign: 'middle' }}
|
||||
size="large"
|
||||
>
|
||||
{avatarLetter}
|
||||
</Avatar>
|
||||
}
|
||||
title={review.username}
|
||||
description={content}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SingleReview;
|
@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { List, Spin, Button } from 'antd';
|
||||
import './styles.css';
|
||||
|
||||
import InfiniteScroll from 'react-infinite-scroller';
|
||||
import SingleReview from './components/Review';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../../../services/utils/errorHandler';
|
||||
|
||||
const limit = 5;
|
||||
|
||||
class Reviews extends React.Component {
|
||||
state = {
|
||||
data: [],
|
||||
loading: false,
|
||||
hasMore: false,
|
||||
loadMore: false,
|
||||
forbiddenErrors: {
|
||||
reviews: false,
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchData(0, limit, res => {
|
||||
this.setState({
|
||||
data: res,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fetchData = (offset, limit, callback) => {
|
||||
const config = this.props.context;
|
||||
|
||||
const { uuid, type } = this.props;
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
axios
|
||||
.get(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/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;
|
||||
forbiddenErrors.reviews = true;
|
||||
this.setState({
|
||||
forbiddenErrors,
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleInfiniteOnLoad = count => {
|
||||
const offset = count * limit;
|
||||
let data = this.state.data;
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
if (data.length > 149) {
|
||||
this.setState({
|
||||
hasMore: false,
|
||||
loading: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.fetchData(offset, limit, res => {
|
||||
if (res.length > 0) {
|
||||
data = data.concat(res);
|
||||
this.setState({
|
||||
data,
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
hasMore: false,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
enableLoading = () => {
|
||||
this.setState({
|
||||
hasMore: true,
|
||||
loadMore: true,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className="demo-infinite-container">
|
||||
<InfiniteScroll
|
||||
initialLoad={false}
|
||||
pageStart={0}
|
||||
loadMore={this.handleInfiniteOnLoad}
|
||||
hasMore={!this.state.loading && this.state.hasMore}
|
||||
useWindow={true}
|
||||
>
|
||||
<List
|
||||
dataSource={this.state.data}
|
||||
renderItem={item => (
|
||||
<List.Item key={item.id}>
|
||||
<SingleReview review={item} />
|
||||
</List.Item>
|
||||
)}
|
||||
>
|
||||
{this.state.loading && this.state.hasMore && (
|
||||
<div className="demo-loading-container">
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(Reviews);
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.demo-infinite-container {
|
||||
overflow: auto;
|
||||
padding: 8px 24px;
|
||||
}
|
||||
.demo-loading-container {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.demo-loading {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 50%;
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { ShopOutlined } from '@ant-design/icons';
|
||||
import { Divider, Row, Col, Typography, Button, Tooltip, Alert } from 'antd';
|
||||
import StarRatings from 'react-star-ratings';
|
||||
import Reviews from './components/Reviews';
|
||||
import '../../../../../../../../App.css';
|
||||
import DetailedRating from '../../../../components/DetailedRating';
|
||||
import EditRelease from './components/EditRelease';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import Authorized from '../../../../../../../../components/Authorized/Authorized';
|
||||
import DeleteRelease from './components/DeleteRelease';
|
||||
import { EntgraIcon } from 'entgra-icons-react';
|
||||
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
class ReleaseView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { app, release } = this.props;
|
||||
const config = this.props.context;
|
||||
const { lifecycle, currentLifecycleStatus } = this.props;
|
||||
let isKeyInclude = false;
|
||||
let metaArrayWithOutWindowsKey = [];
|
||||
if (release == null) {
|
||||
return null;
|
||||
}
|
||||
let isAppUpdatable,
|
||||
isAppInstallable,
|
||||
isDeletableState = false;
|
||||
if (lifecycle != null) {
|
||||
isAppUpdatable = lifecycle[currentLifecycleStatus].isAppUpdatable;
|
||||
isAppInstallable = lifecycle[currentLifecycleStatus].isAppInstallable;
|
||||
isDeletableState = lifecycle[currentLifecycleStatus].isDeletableState;
|
||||
}
|
||||
|
||||
const platform = app.deviceType;
|
||||
const defaultPlatformIcons = config.defaultPlatformIcons;
|
||||
let icon = defaultPlatformIcons.default.icon;
|
||||
let color = defaultPlatformIcons.default.color;
|
||||
|
||||
if (defaultPlatformIcons.hasOwnProperty(platform)) {
|
||||
icon = defaultPlatformIcons[platform].icon;
|
||||
color = defaultPlatformIcons[platform].color;
|
||||
}
|
||||
let metaData = [];
|
||||
try {
|
||||
metaData = JSON.parse(release.metaData);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="release">
|
||||
<Row>
|
||||
<Col xl={4} sm={6} xs={8} className="release-icon">
|
||||
<img src={release.iconPath} alt="icon" />
|
||||
</Col>
|
||||
<Col xl={10} sm={11} className="release-title">
|
||||
<Title level={2}>{app.name}</Title>
|
||||
<StarRatings
|
||||
rating={release.rating}
|
||||
starRatedColor="#777"
|
||||
starDimension="20px"
|
||||
starSpacing="2px"
|
||||
numberOfStars={5}
|
||||
name="rating"
|
||||
/>
|
||||
<br />
|
||||
<Text>Platform : </Text>
|
||||
<span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
|
||||
<EntgraIcon type={icon} />
|
||||
</span>
|
||||
<Divider type="vertical" />
|
||||
<Text>Version : {release.version}</Text>
|
||||
<br />
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={
|
||||
<>
|
||||
<EditRelease
|
||||
isAppUpdatable={isAppUpdatable}
|
||||
type={app.type}
|
||||
deviceType={app.deviceType}
|
||||
release={release}
|
||||
updateRelease={this.props.updateRelease}
|
||||
supportedOsVersions={[...this.props.supportedOsVersions]}
|
||||
/>
|
||||
<Divider type="vertical" />
|
||||
<DeleteRelease
|
||||
uuid={release.uuid}
|
||||
isDeletableState={isDeletableState}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col xl={8} md={10} sm={24} xs={24} style={{ float: 'right' }}>
|
||||
<div>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={
|
||||
<Tooltip
|
||||
title={
|
||||
isAppInstallable
|
||||
? 'Open this app in store'
|
||||
: "This release isn't in an installable state"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
style={{ float: 'right' }}
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
icon={<ShopOutlined />}
|
||||
disabled={!isAppInstallable}
|
||||
onClick={() => {
|
||||
window.open(
|
||||
window.location.origin +
|
||||
'/store/' +
|
||||
app.deviceType +
|
||||
'/apps/' +
|
||||
release.uuid,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Open in store
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider />
|
||||
<Row className="release-images" style={{ flexFlow: 'nowrap' }}>
|
||||
{release.screenshots.map((screenshotUrl, index) => {
|
||||
return (
|
||||
<div key={index} className="release-screenshot">
|
||||
<img key={screenshotUrl} src={screenshotUrl} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
<Divider />
|
||||
<Paragraph type="secondary" ellipsis={{ rows: 3, expandable: true }}>
|
||||
{release.description}
|
||||
</Paragraph>
|
||||
<Divider />
|
||||
<Text>META DATA</Text>
|
||||
<Row>
|
||||
{metaData.map((data, index) => {
|
||||
// Exclude showing the values related to windows app type variables in the metaData UI
|
||||
if (
|
||||
!config.windowsAppxMsiKeyValueForMetaData.metaKeyArray.includes(
|
||||
data.key,
|
||||
)
|
||||
) {
|
||||
isKeyInclude = false;
|
||||
metaArrayWithOutWindowsKey.push(data);
|
||||
return (
|
||||
<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 ||
|
||||
(!isKeyInclude && metaArrayWithOutWindowsKey.length === 0)) && (
|
||||
<Text type="secondary">No meta data available.</Text>
|
||||
)}
|
||||
</Row>
|
||||
<Divider />
|
||||
<Text>REVIEWS</Text>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/admin/review/view"
|
||||
yes={
|
||||
<div>
|
||||
<Row>
|
||||
<Col lg={18}>
|
||||
<DetailedRating type="release" uuid={release.uuid} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Reviews type="release" uuid={release.uuid} />
|
||||
</div>
|
||||
}
|
||||
no={
|
||||
<Alert
|
||||
message="You don't have permission to view reviews."
|
||||
type="warning"
|
||||
banner
|
||||
closable
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ReleaseView);
|
@ -1,250 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import '../../../../../../App.css';
|
||||
import { Typography, Row, Col, Card, Skeleton } from 'antd';
|
||||
import axios from 'axios';
|
||||
import ReleaseView from './components/ReleaseView';
|
||||
import LifeCycle from './components/LifeCycle';
|
||||
import { withConfigContext } from '../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../services/utils/errorHandler';
|
||||
import Authorized from '../../../../../../components/Authorized/Authorized';
|
||||
import { isAuthorized } from '../../../../../../services/utils/authorizationHandler';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
class Release extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.routes = props.routes;
|
||||
this.state = {
|
||||
loading: true,
|
||||
app: null,
|
||||
uuid: null,
|
||||
release: null,
|
||||
currentLifecycleStatus: null,
|
||||
lifecycle: null,
|
||||
supportedOsVersions: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { uuid } = this.props.match.params;
|
||||
this.fetchData(uuid);
|
||||
if (
|
||||
isAuthorized(
|
||||
this.props.context.user,
|
||||
'/permission/admin/app-mgt/publisher/application/update',
|
||||
)
|
||||
) {
|
||||
this.getLifecycle();
|
||||
}
|
||||
}
|
||||
|
||||
changeCurrentLifecycleStatus = status => {
|
||||
this.setState({
|
||||
currentLifecycleStatus: status,
|
||||
});
|
||||
};
|
||||
|
||||
updateRelease = release => {
|
||||
this.setState({
|
||||
release,
|
||||
});
|
||||
};
|
||||
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
const app = res.data.data;
|
||||
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,
|
||||
});
|
||||
if (config.deviceTypes.mobileTypes.includes(app.deviceType)) {
|
||||
if (
|
||||
isAuthorized(
|
||||
config.user,
|
||||
'/permission/admin/device-mgt/admin/device-type',
|
||||
)
|
||||
) {
|
||||
this.getSupportedOsVersions(app.deviceType);
|
||||
} else {
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
const lifecycle = res.data.data;
|
||||
this.setState({
|
||||
lifecycle: lifecycle,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load lifecycle configuration.',
|
||||
true,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
let supportedOsVersions = JSON.parse(res.data.data);
|
||||
this.setState({
|
||||
supportedOsVersions,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load supported OS versions.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
app,
|
||||
release,
|
||||
currentLifecycleStatus,
|
||||
lifecycle,
|
||||
loading,
|
||||
forbiddenErrors,
|
||||
} = this.state;
|
||||
|
||||
if (release == null && loading === false) {
|
||||
return (
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 780 }}>
|
||||
<Title level={3}>No Apps Found</Title>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// todo remove uppercase
|
||||
return (
|
||||
<div>
|
||||
<div className="main-container">
|
||||
<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 && (
|
||||
<ReleaseView
|
||||
forbiddenErrors={forbiddenErrors}
|
||||
app={app}
|
||||
release={release}
|
||||
currentLifecycleStatus={currentLifecycleStatus}
|
||||
lifecycle={lifecycle}
|
||||
updateRelease={this.updateRelease}
|
||||
supportedOsVersions={[...this.state.supportedOsVersions]}
|
||||
/>
|
||||
)}
|
||||
</Skeleton>
|
||||
</Card>
|
||||
</Col>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={
|
||||
<Col lg={8} md={24} style={{ padding: 3 }}>
|
||||
<Card lg={8} md={24}>
|
||||
<Skeleton loading={loading} active paragraph={{ rows: 8 }}>
|
||||
{release !== null && (
|
||||
<LifeCycle
|
||||
uuid={release.uuid}
|
||||
currentStatus={release.currentStatus.toUpperCase()}
|
||||
changeCurrentLifecycleStatus={
|
||||
this.changeCurrentLifecycleStatus
|
||||
}
|
||||
lifecycle={lifecycle}
|
||||
appReleases={this.props.location.state}
|
||||
/>
|
||||
)}
|
||||
</Skeleton>
|
||||
</Card>
|
||||
</Col>
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(Release);
|
@ -1,520 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Card,
|
||||
Tag,
|
||||
message,
|
||||
Input,
|
||||
notification,
|
||||
Divider,
|
||||
Button,
|
||||
Spin,
|
||||
Tooltip,
|
||||
Popconfirm,
|
||||
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 '../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../services/utils/errorHandler';
|
||||
import { isAuthorized } from '../../../../../../services/utils/authorizationHandler';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
class ManageCategories extends React.Component {
|
||||
state = {
|
||||
loading: false,
|
||||
searchText: '',
|
||||
categories: [],
|
||||
tempElements: [],
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
isAddNewVisible: false,
|
||||
isEditModalVisible: false,
|
||||
currentlyEditingId: null,
|
||||
editingValue: null,
|
||||
forbiddenErrors: {
|
||||
categories: false,
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const config = this.props.context;
|
||||
this.hasPermissionToManage = isAuthorized(
|
||||
config.user,
|
||||
'/permission/admin/app-mgt/publisher/admin/application/update',
|
||||
);
|
||||
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,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occured while trying to load categories',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleCloseButton = () => {
|
||||
this.setState({
|
||||
tempElements: [],
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
deleteCategory = id => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
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!',
|
||||
});
|
||||
|
||||
const { categories } = this.state;
|
||||
const remainingElements = categories.filter(function(value) {
|
||||
return value.categoryName !== id;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
categories: remainingElements,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load categories.',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
renderElement = category => {
|
||||
const config = this.props.context;
|
||||
const categoryName = category.categoryName;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
color={pSBC(0.3, config.theme.primaryColor)}
|
||||
style={{ marginTop: 8 }}
|
||||
>
|
||||
{categoryName}
|
||||
{this.hasPermissionToManage && (
|
||||
<>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="edit">
|
||||
<EditOutlined
|
||||
onClick={() => {
|
||||
this.openEditModal(categoryName);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="delete">
|
||||
<Popconfirm
|
||||
title="Are you sure delete this category?"
|
||||
onConfirm={() => {
|
||||
if (category.isCategoryDeletable) {
|
||||
this.deleteCategory(categoryName);
|
||||
} else {
|
||||
notification.error({
|
||||
message: 'Cannot delete "' + categoryName + '"',
|
||||
description:
|
||||
'This category is currently used. Please unassign the category from apps.',
|
||||
});
|
||||
}
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={category.categoryName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
renderTempElement = category => {
|
||||
const tagElem = (
|
||||
<Tag
|
||||
style={{ marginTop: 8 }}
|
||||
closable
|
||||
onClose={e => {
|
||||
e.preventDefault();
|
||||
const { tempElements } = this.state;
|
||||
const remainingElements = tempElements.filter(function(value) {
|
||||
return value.categoryName !== category.categoryName;
|
||||
});
|
||||
this.setState({
|
||||
tempElements: remainingElements,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{category.categoryName}
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={category.categoryName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
showInput = () => {
|
||||
this.setState({ inputVisible: true }, () => this.input.focus());
|
||||
};
|
||||
|
||||
handleInputChange = e => {
|
||||
this.setState({ inputValue: e.target.value });
|
||||
};
|
||||
|
||||
handleInputConfirm = () => {
|
||||
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 },
|
||||
];
|
||||
} else {
|
||||
message.warning('Category already exists');
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
tempElements,
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
});
|
||||
};
|
||||
|
||||
handleSave = () => {
|
||||
const config = this.props.context;
|
||||
const { tempElements, categories } = this.state;
|
||||
this.setState({
|
||||
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',
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'New Categories were added successfully',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
categories: [...categories, ...tempElements],
|
||||
tempElements: [],
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
loading: false,
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to add categories.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
saveInputRef = input => (this.input = input);
|
||||
|
||||
closeEditModal = e => {
|
||||
this.setState({
|
||||
isEditModalVisible: false,
|
||||
currentlyEditingId: null,
|
||||
});
|
||||
};
|
||||
|
||||
openEditModal = id => {
|
||||
this.setState({
|
||||
isEditModalVisible: true,
|
||||
currentlyEditingId: id,
|
||||
editingValue: id,
|
||||
});
|
||||
};
|
||||
|
||||
editItem = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
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,
|
||||
{},
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Category was edited successfully',
|
||||
});
|
||||
|
||||
categories[
|
||||
categories.findIndex(i => i.categoryName === currentlyEditingId)
|
||||
].categoryName = editingValue;
|
||||
|
||||
this.setState({
|
||||
categories: categories,
|
||||
loading: false,
|
||||
editingValue: null,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to delete the category.',
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
editingValue: null,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handleEditInputChange = e => {
|
||||
this.setState({
|
||||
editingValue: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
categories,
|
||||
inputVisible,
|
||||
inputValue,
|
||||
tempElements,
|
||||
isAddNewVisible,
|
||||
} = this.state;
|
||||
const categoriesElements = categories.map(this.renderElement);
|
||||
const temporaryElements = tempElements.map(this.renderTempElement);
|
||||
return (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
{!this.hasPermissionToManage && (
|
||||
<Alert
|
||||
message="You don't have permission to add / edit / delete categories."
|
||||
type="warning"
|
||||
banner
|
||||
/>
|
||||
)}
|
||||
<Card>
|
||||
<Spin tip="Working on it..." spinning={this.state.loading}>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<Title level={4}>Categories</Title>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isAddNewVisible && (
|
||||
<div style={{ float: 'right' }}>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
// type="primary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
this.setState(
|
||||
{
|
||||
isAddNewVisible: true,
|
||||
inputVisible: true,
|
||||
},
|
||||
() => this.input.focus(),
|
||||
);
|
||||
}}
|
||||
htmlType="button"
|
||||
disabled={!this.hasPermissionToManage}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
{isAddNewVisible && (
|
||||
<div>
|
||||
<Divider />
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
type: 'from',
|
||||
duration: 100,
|
||||
onComplete: e => {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{temporaryElements}
|
||||
|
||||
{inputVisible && (
|
||||
<Input
|
||||
ref={this.saveInputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
style={{ width: 120 }}
|
||||
value={inputValue}
|
||||
onChange={this.handleInputChange}
|
||||
onBlur={this.handleInputConfirm}
|
||||
onPressEnter={this.handleInputConfirm}
|
||||
/>
|
||||
)}
|
||||
{!inputVisible && (
|
||||
<Tag
|
||||
onClick={this.showInput}
|
||||
style={{ background: '#fff', borderStyle: 'dashed' }}
|
||||
>
|
||||
<PlusOutlined /> New Category
|
||||
</Tag>
|
||||
)}
|
||||
</TweenOneGroup>
|
||||
</div>
|
||||
<div>
|
||||
{tempElements.length > 0 && (
|
||||
<span>
|
||||
<Button
|
||||
onClick={this.handleSave}
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
size="small"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
</span>
|
||||
)}
|
||||
<Button onClick={this.handleCloseButton} size="small">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Divider dashed="true" />
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
type: 'from',
|
||||
duration: 100,
|
||||
onComplete: e => {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{categoriesElements}
|
||||
</TweenOneGroup>
|
||||
</div>
|
||||
</Spin>
|
||||
</Card>
|
||||
<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}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ManageCategories);
|
@ -1,512 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Card,
|
||||
Tag,
|
||||
message,
|
||||
Input,
|
||||
notification,
|
||||
Divider,
|
||||
Button,
|
||||
Spin,
|
||||
Tooltip,
|
||||
Popconfirm,
|
||||
Modal,
|
||||
Row,
|
||||
Col,
|
||||
Typography,
|
||||
Alert,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import { TweenOneGroup } from 'rc-tween-one';
|
||||
import { withConfigContext } from '../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../services/utils/errorHandler';
|
||||
import { isAuthorized } from '../../../../../../services/utils/authorizationHandler';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
class ManageTags extends React.Component {
|
||||
state = {
|
||||
loading: false,
|
||||
searchText: '',
|
||||
tags: [],
|
||||
tempElements: [],
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
isAddNewVisible: false,
|
||||
isEditModalVisible: false,
|
||||
currentlyEditingId: null,
|
||||
editingValue: null,
|
||||
forbiddenErrors: {
|
||||
tags: false,
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const config = this.props.context;
|
||||
this.hasPermissionToManage = isAuthorized(
|
||||
config.user,
|
||||
'/permission/admin/app-mgt/publisher/admin/application/update',
|
||||
);
|
||||
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,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to load tags.',
|
||||
true,
|
||||
);
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleCloseButton = () => {
|
||||
this.setState({
|
||||
tempElements: [],
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
};
|
||||
|
||||
deleteTag = id => {
|
||||
const config = this.props.context;
|
||||
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
|
||||
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!',
|
||||
});
|
||||
|
||||
const { tags } = this.state;
|
||||
const remainingElements = tags.filter(function(value) {
|
||||
return value.tagName !== id;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
tags: remainingElements,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to delete the tag.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
renderElement = tag => {
|
||||
const tagName = tag.tagName;
|
||||
const tagElem = (
|
||||
<Tag color="#34495e" style={{ marginTop: 8 }}>
|
||||
{tagName}
|
||||
{this.hasPermissionToManage && (
|
||||
<>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="edit">
|
||||
<EditOutlined
|
||||
onClick={() => {
|
||||
this.openEditModal(tagName);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip title="delete">
|
||||
<Popconfirm
|
||||
title="Are you sure delete this tag?"
|
||||
onConfirm={() => {
|
||||
if (tag.isTagDeletable) {
|
||||
this.deleteTag(tagName);
|
||||
} else {
|
||||
notification.error({
|
||||
message: 'Cannot delete "' + tagName + '"',
|
||||
description:
|
||||
'This tag is currently used. Please unassign the tag from apps.',
|
||||
});
|
||||
}
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={tag.tagName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
renderTempElement = tag => {
|
||||
const { tempElements } = this.state;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
style={{ marginTop: 8 }}
|
||||
closable
|
||||
onClose={e => {
|
||||
e.preventDefault();
|
||||
const remainingElements = tempElements.filter(function(value) {
|
||||
return value.tagName !== tag.tagName;
|
||||
});
|
||||
this.setState({
|
||||
tempElements: remainingElements,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{tag.tagName}
|
||||
</Tag>
|
||||
);
|
||||
return (
|
||||
<span key={tag.tagName} style={{ display: 'inline-block' }}>
|
||||
{tagElem}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
showInput = () => {
|
||||
this.setState({ inputVisible: true }, () => this.input.focus());
|
||||
};
|
||||
|
||||
handleInputChange = e => {
|
||||
this.setState({ inputValue: e.target.value });
|
||||
};
|
||||
|
||||
handleInputConfirm = () => {
|
||||
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 },
|
||||
];
|
||||
} else {
|
||||
message.warning('Tag already exists');
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
tempElements,
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
});
|
||||
};
|
||||
|
||||
handleSave = () => {
|
||||
const config = this.props.context;
|
||||
const { tempElements, tags } = this.state;
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const data = tempElements.map(tag => tag.tagName);
|
||||
|
||||
axios
|
||||
.post(
|
||||
window.location.origin +
|
||||
config.serverConfig.invoker.uri +
|
||||
config.serverConfig.invoker.publisher +
|
||||
'/applications/tags',
|
||||
data,
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'New tags were added successfully',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
tags: [...tags, ...tempElements],
|
||||
tempElements: [],
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
loading: false,
|
||||
isAddNewVisible: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to delete tag.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
saveInputRef = input => (this.input = input);
|
||||
|
||||
closeEditModal = e => {
|
||||
this.setState({
|
||||
isEditModalVisible: false,
|
||||
currentlyEditingId: null,
|
||||
});
|
||||
};
|
||||
|
||||
openEditModal = id => {
|
||||
this.setState({
|
||||
isEditModalVisible: true,
|
||||
currentlyEditingId: id,
|
||||
editingValue: id,
|
||||
});
|
||||
};
|
||||
|
||||
editItem = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
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,
|
||||
{},
|
||||
)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Done!',
|
||||
description: 'Tag was edited successfully',
|
||||
});
|
||||
|
||||
tags[
|
||||
tags.findIndex(i => i.tagName === currentlyEditingId)
|
||||
].tagName = editingValue;
|
||||
|
||||
this.setState({
|
||||
tags: tags,
|
||||
loading: false,
|
||||
editingValue: null,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while trying to edit tag.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
editingValue: null,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handleEditInputChange = e => {
|
||||
this.setState({
|
||||
editingValue: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
tags,
|
||||
inputVisible,
|
||||
inputValue,
|
||||
tempElements,
|
||||
isAddNewVisible,
|
||||
} = this.state;
|
||||
const tagsElements = tags.map(this.renderElement);
|
||||
const temporaryElements = tempElements.map(this.renderTempElement);
|
||||
return (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
{!this.hasPermissionToManage && (
|
||||
<Alert
|
||||
message="You don't have permission to add / edit / delete tags."
|
||||
type="warning"
|
||||
banner
|
||||
/>
|
||||
)}
|
||||
<Card>
|
||||
<Spin tip="Working on it..." spinning={this.state.loading}>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<Title level={4}>Tags</Title>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isAddNewVisible && (
|
||||
<div style={{ float: 'right' }}>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
// type="primary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
this.setState(
|
||||
{
|
||||
isAddNewVisible: true,
|
||||
inputVisible: true,
|
||||
},
|
||||
() => this.input.focus(),
|
||||
);
|
||||
}}
|
||||
htmlType="button"
|
||||
disabled={!this.hasPermissionToManage}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
{isAddNewVisible && (
|
||||
<div>
|
||||
<Divider />
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
type: 'from',
|
||||
duration: 100,
|
||||
onComplete: e => {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{temporaryElements}
|
||||
|
||||
{inputVisible && (
|
||||
<Input
|
||||
ref={this.saveInputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
style={{ width: 120 }}
|
||||
value={inputValue}
|
||||
onChange={this.handleInputChange}
|
||||
onBlur={this.handleInputConfirm}
|
||||
onPressEnter={this.handleInputConfirm}
|
||||
/>
|
||||
)}
|
||||
{!inputVisible && (
|
||||
<Tag
|
||||
onClick={this.showInput}
|
||||
style={{ background: '#fff', borderStyle: 'dashed' }}
|
||||
>
|
||||
<PlusOutlined /> New Tag
|
||||
</Tag>
|
||||
)}
|
||||
</TweenOneGroup>
|
||||
</div>
|
||||
<div>
|
||||
{tempElements.length > 0 && (
|
||||
<span>
|
||||
<Button
|
||||
onClick={this.handleSave}
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
size="small"
|
||||
disabled={tempElements.length === 0}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
</span>
|
||||
)}
|
||||
<Button onClick={this.handleCloseButton} size="small">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Divider dashed="true" />
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<TweenOneGroup
|
||||
enter={{
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
type: 'from',
|
||||
duration: 100,
|
||||
onComplete: e => {
|
||||
e.target.style = '';
|
||||
},
|
||||
}}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
appear={false}
|
||||
>
|
||||
{tagsElements}
|
||||
</TweenOneGroup>
|
||||
</div>
|
||||
</Spin>
|
||||
</Card>
|
||||
<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}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ManageTags);
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Typography, Breadcrumb, Row, Col } from 'antd';
|
||||
import ManageCategories from './components/Categories';
|
||||
import ManageTags from './components/Tags';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Authorized from '../../../../components/Authorized/Authorized';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
class Manage extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.routes = props.routes;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> 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>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 780 }}>
|
||||
<Row gutter={16}>
|
||||
<Authorized
|
||||
permission="/permission/admin/app-mgt/publisher/application/update"
|
||||
yes={
|
||||
<>
|
||||
<Col sm={24} md={12}>
|
||||
<ManageCategories />
|
||||
</Col>
|
||||
<Col sm={24} md={12}>
|
||||
<ManageTags />
|
||||
</Col>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Manage;
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Modal, Button } from 'antd';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
|
||||
class GooglePlayIframe extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.config = this.props.context;
|
||||
|
||||
this.state = {
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ display: 'inline-block', padding: 4 }}>
|
||||
<Button type="primary" onClick={this.showModal}>
|
||||
Approve Applications
|
||||
</Button>
|
||||
<Modal
|
||||
title={null}
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
width={740}
|
||||
footer={null}
|
||||
>
|
||||
<iframe
|
||||
style={{
|
||||
height: 720,
|
||||
border: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
src={
|
||||
'https://play.google.com/work/embedded/search?token=' +
|
||||
this.config.androidEnterpriseToken +
|
||||
'&mode=APPROVE&showsearchbox=TRUE'
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(GooglePlayIframe);
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Divider, Input, Modal, notification, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
import { withRouter } from 'react-router';
|
||||
import { handleApiError } from '../../../../../../../../../../services/utils/errorHandler';
|
||||
|
||||
class AddNewPage extends React.Component {
|
||||
state = {
|
||||
visible: false,
|
||||
pageName: '',
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
loading: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handlePageName = e => {
|
||||
this.setState({
|
||||
pageName: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
createNewPage = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({ loading: true });
|
||||
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
const { pageId, pageName } = res.data.data;
|
||||
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Page created successfully!',
|
||||
});
|
||||
|
||||
this.setState({ loading: false });
|
||||
|
||||
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 });
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ marginTop: 24, marginBottom: 24 }}>
|
||||
<Button type="dashed" onClick={this.showModal}>
|
||||
Add new page
|
||||
</Button>
|
||||
<Modal
|
||||
title="Add new page"
|
||||
visible={this.state.visible}
|
||||
onOk={this.createNewPage}
|
||||
onCancel={this.handleCancel}
|
||||
okText="Create Page"
|
||||
footer={null}
|
||||
>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<p>Choose a name for the page</p>
|
||||
<Input onChange={this.handlePageName} />
|
||||
<Divider />
|
||||
<div>
|
||||
<Button onClick={this.handleCancel}>Cancel</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
onClick={this.createNewPage}
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
disabled={this.state.pageName.length === 0}
|
||||
>
|
||||
Create Page
|
||||
</Button>
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(AddNewPage));
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.layout-pages .action {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layout-pages .edit {
|
||||
color: #008dff;
|
||||
}
|
||||
|
||||
.layout-pages .btn-warning {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.layout-pages .btn-warning:hover {
|
||||
color: #fa8905;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button, notification } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
|
||||
|
||||
class SyncAndroidApps extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
syncApps = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
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!',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(error, 'Error occurred while syncing the apps.');
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading } = this.state;
|
||||
return (
|
||||
<div style={{ display: 'inline-block', padding: 4 }}>
|
||||
<Button
|
||||
onClick={this.syncApps}
|
||||
loading={loading}
|
||||
style={{ marginTop: 16 }}
|
||||
type="primary"
|
||||
icon={<SyncOutlined />}
|
||||
>
|
||||
Sync{loading && 'ing...'}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(SyncAndroidApps);
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined } from '@ant-design/icons';
|
||||
import { PageHeader, Breadcrumb, Divider, Result } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import SyncAndroidApps from './components/SyncAndroidApps';
|
||||
import { withConfigContext } from '../../../../../../components/ConfigContext';
|
||||
import GooglePlayIframe from './components/GooglePlayIframe';
|
||||
import Pages from './components/Pages';
|
||||
import Authorized from '../../../../../../components/Authorized/Authorized';
|
||||
|
||||
class ManageAndroidEnterprise extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.routes = props.routes;
|
||||
this.config = this.props.context;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> 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>*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/enterprise/user/view"
|
||||
yes={
|
||||
<>
|
||||
<SyncAndroidApps />
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/enterprise/user/modify"
|
||||
yes={<GooglePlayIframe />}
|
||||
/>
|
||||
<Divider />
|
||||
<Pages />
|
||||
</>
|
||||
}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to view android enterprise configurations."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ManageAndroidEnterprise);
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Modal, Table, Avatar } from 'antd';
|
||||
import '../../styles.css';
|
||||
import { withConfigContext } from '../../../../../../../../../../../../components/ConfigContext';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'iconUrl',
|
||||
key: 'iconUrl',
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: iconUrl => <Avatar shape="square" src={iconUrl} />,
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Page',
|
||||
dataIndex: 'packageId',
|
||||
key: 'packageId',
|
||||
},
|
||||
];
|
||||
|
||||
class AddAppsToClusterModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: false,
|
||||
loading: false,
|
||||
selectedProducts: [],
|
||||
homePageId: null,
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = () => {
|
||||
this.props.addSelectedProducts(this.state.selectedProducts);
|
||||
this.handleCancel();
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
rowSelection = {
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
this.setState({
|
||||
selectedProducts: selectedRows,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
const { pagination, loading } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<div className="btn-add-new-wrapper">
|
||||
<div className="btn-add-new">
|
||||
<button className="btn" onClick={this.showModal}>
|
||||
<PlusOutlined style={{ position: 'relative' }} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="title">Add app</div>
|
||||
</div>
|
||||
<Modal
|
||||
title="Select Apps"
|
||||
width={640}
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey={record => record.packageId}
|
||||
dataSource={this.props.unselectedProducts}
|
||||
scroll={{ x: 300 }}
|
||||
pagination={{
|
||||
...pagination,
|
||||
size: 'small',
|
||||
// position: "top",
|
||||
showTotal: (total, range) =>
|
||||
`showing ${range[0]}-${range[1]} of ${total} pages`,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
loading={loading}
|
||||
onChange={this.handleTableChange}
|
||||
rowSelection={this.rowSelection}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(AddAppsToClusterModal);
|
@ -1,483 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
CaretDownOutlined,
|
||||
CaretLeftFilled,
|
||||
CaretRightFilled,
|
||||
CaretUpOutlined,
|
||||
CloseCircleFilled,
|
||||
DeleteOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Divider,
|
||||
message,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Spin,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
|
||||
import './styles.css';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
import AddAppsToClusterModal from './components/AddAppsToClusterModal';
|
||||
import { handleApiError } from '../../../../../../../../../../services/utils/errorHandler';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
class Cluster extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { cluster, pageId } = this.props;
|
||||
this.originalCluster = Object.assign({}, cluster);
|
||||
const { name, products, clusterId } = cluster;
|
||||
this.clusterId = clusterId;
|
||||
this.pageId = pageId;
|
||||
this.state = {
|
||||
name,
|
||||
products,
|
||||
isSaveable: false,
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleNameChange = name => {
|
||||
this.setState({
|
||||
name,
|
||||
});
|
||||
if (name !== this.originalCluster.name) {
|
||||
this.setState({
|
||||
isSaveable: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
isProductsChanged = currentProducts => {
|
||||
let isChanged = false;
|
||||
const originalProducts = this.originalCluster.products;
|
||||
if (currentProducts.length === originalProducts.length) {
|
||||
for (let i = 0; i < currentProducts.length; i++) {
|
||||
if (currentProducts[i].packageId !== originalProducts[i].packageId) {
|
||||
isChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isChanged = true;
|
||||
}
|
||||
return isChanged;
|
||||
};
|
||||
|
||||
swapProduct = (index, swapIndex) => {
|
||||
const products = [...this.state.products];
|
||||
if (swapIndex !== -1 && index < products.length) {
|
||||
// swap elements
|
||||
[products[index], products[swapIndex]] = [
|
||||
products[swapIndex],
|
||||
products[index],
|
||||
];
|
||||
|
||||
this.setState({
|
||||
products,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
isSaveable: this.isProductsChanged(products),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
removeProduct = index => {
|
||||
const products = [...this.state.products];
|
||||
products.splice(index, 1);
|
||||
this.setState({
|
||||
products,
|
||||
isSaveable: true,
|
||||
});
|
||||
};
|
||||
|
||||
getCurrentCluster = () => {
|
||||
const { products, name } = this.state;
|
||||
return {
|
||||
pageId: this.pageId,
|
||||
clusterId: this.clusterId,
|
||||
name: name,
|
||||
products: products,
|
||||
orderInPage: this.props.orderInPage,
|
||||
};
|
||||
};
|
||||
|
||||
resetChanges = () => {
|
||||
const cluster = this.originalCluster;
|
||||
const { name, products } = cluster;
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
name,
|
||||
products,
|
||||
isSaveable: false,
|
||||
});
|
||||
};
|
||||
|
||||
updateCluster = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
const cluster = this.getCurrentCluster();
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Cluster updated successfully!',
|
||||
});
|
||||
const cluster = res.data.data;
|
||||
|
||||
this.originalCluster = Object.assign({}, cluster);
|
||||
|
||||
this.resetChanges();
|
||||
if (this.props.toggleAddNewClusterVisibility !== undefined) {
|
||||
this.props.toggleAddNewClusterVisibility(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 });
|
||||
|
||||
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({
|
||||
message: 'Done!',
|
||||
description: 'Cluster deleted successfully!',
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
|
||||
this.props.removeLoadedCluster(this.clusterId);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the cluster.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
getUnselectedProducts = () => {
|
||||
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 => {
|
||||
for (let i = 0; i < unSelectedProducts.length; i++) {
|
||||
if (selectedProduct.packageId === unSelectedProducts[i].packageId) {
|
||||
// remove item from array
|
||||
unSelectedProducts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return unSelectedProducts;
|
||||
};
|
||||
|
||||
addSelectedProducts = products => {
|
||||
this.setState({
|
||||
products: [...this.state.products, ...products],
|
||||
isSaveable: products.length > 0,
|
||||
});
|
||||
};
|
||||
|
||||
cancelAddingNewCluster = () => {
|
||||
this.resetChanges();
|
||||
this.props.toggleAddNewClusterVisibility(false);
|
||||
};
|
||||
|
||||
saveNewCluster = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
const cluster = this.getCurrentCluster();
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Cluster updated successfully!',
|
||||
});
|
||||
|
||||
const cluster = res.data.data;
|
||||
|
||||
this.resetChanges();
|
||||
this.props.addSavedClusterToThePage(cluster);
|
||||
}
|
||||
})
|
||||
.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',
|
||||
duration: 0,
|
||||
description: 'Error occurred while trying to update the cluster.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
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 iconUrl = product.iconUrl;
|
||||
// check if the icon url is an url or google image id
|
||||
if (iconUrl.startsWith('http')) {
|
||||
imageSrc = iconUrl;
|
||||
} else {
|
||||
imageSrc = `https://lh3.googleusercontent.com/${iconUrl}=s240-rw`;
|
||||
}
|
||||
return (
|
||||
<div className="product">
|
||||
<div className="arrow">
|
||||
{this.props.hasPermissionToManage && (
|
||||
<button
|
||||
disabled={index === 0}
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
this.swapProduct(index, index - 1);
|
||||
}}
|
||||
>
|
||||
<CaretLeftFilled />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="product-icon">
|
||||
<img src={imageSrc} />
|
||||
<Tooltip title={packageId}>
|
||||
<div className="title">{packageId}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{this.props.hasPermissionToManage && (
|
||||
<>
|
||||
<div className="arrow">
|
||||
<button
|
||||
disabled={index === products.length - 1}
|
||||
onClick={() => {
|
||||
this.swapProduct(index, index + 1);
|
||||
}}
|
||||
className="btn btn-right"
|
||||
>
|
||||
<CaretRightFilled />
|
||||
</button>
|
||||
</div>
|
||||
<div className="delete-btn">
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
this.removeProduct(index);
|
||||
}}
|
||||
>
|
||||
<CloseCircleFilled />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="cluster" id={this.props.orderInPage}>
|
||||
<Spin spinning={loading}>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
{this.props.hasPermissionToManage && (
|
||||
<Title editable={{ onChange: this.handleNameChange }} level={4}>
|
||||
{name}
|
||||
</Title>
|
||||
)}
|
||||
{!this.props.hasPermissionToManage && (
|
||||
<Title level={4}>{name}</Title>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isTemporary && this.props.hasPermissionToManage && (
|
||||
<div style={{ float: 'right' }}>
|
||||
<Tooltip title="Move Up">
|
||||
<Button
|
||||
type="link"
|
||||
icon={<CaretUpOutlined />}
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.props.swapClusters(index, index - 1);
|
||||
}}
|
||||
htmlType="button"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Move Down">
|
||||
<Button
|
||||
type="link"
|
||||
icon={<CaretDownOutlined />}
|
||||
size="large"
|
||||
onClick={() => {
|
||||
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}
|
||||
>
|
||||
<Button
|
||||
type="danger"
|
||||
icon={<DeleteOutlined />}
|
||||
shape="circle"
|
||||
htmlType="button"
|
||||
/>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="products-row">
|
||||
{this.props.hasPermissionToManage && (
|
||||
<AddAppsToClusterModal
|
||||
addSelectedProducts={this.addSelectedProducts}
|
||||
unselectedProducts={unselectedProducts}
|
||||
/>
|
||||
)}
|
||||
{products.map((product, index) => {
|
||||
return (
|
||||
<Product
|
||||
key={product.packageId}
|
||||
product={product}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{this.props.hasPermissionToManage && (
|
||||
<Row>
|
||||
<Col>
|
||||
{isTemporary && (
|
||||
<div>
|
||||
<Button onClick={this.cancelAddingNewCluster}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
<Tooltip
|
||||
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"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
{!isTemporary && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.resetChanges}
|
||||
disabled={!this.state.isSaveable}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
onClick={this.updateCluster}
|
||||
htmlType="button"
|
||||
type="primary"
|
||||
disabled={!this.state.isSaveable}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(Cluster);
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.cluster{
|
||||
border-radius: 5px;
|
||||
background-color: white;
|
||||
padding: 24px;
|
||||
margin: 14px 0 14px 0;
|
||||
}
|
||||
|
||||
.cluster .products-row{
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.cluster .product{
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
padding: 20px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cluster .product .product-icon{
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
height: 180px;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.cluster .product .title, .cluster .btn-add-new-wrapper .title {
|
||||
color: #202124;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.cluster .product .product-icon img{
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
height: 90px;
|
||||
/*padding-bottom: 16px;*/
|
||||
width: 90px;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.cluster .product .arrow {
|
||||
color: #80868b;
|
||||
font-size: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.cluster .product .arrow .btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 12px;
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
fill: currentColor;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.cluster .product .arrow .btn-right {
|
||||
left: -12px;
|
||||
}
|
||||
|
||||
.cluster .product .delete-btn {
|
||||
color: #80868b;
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
.cluster .product .delete-btn .btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 16px 28px 0 0;
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
fill: currentColor;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new-wrapper{
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
height: 180px;
|
||||
width: 90px;
|
||||
margin: 0 33px;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new{
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
height: 90px;
|
||||
padding-bottom: 16px;
|
||||
width: 90px;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new .btn{
|
||||
height: 36px;
|
||||
padding: 0 23px 0 23px;
|
||||
border-width: 1px;
|
||||
min-height: 90px;
|
||||
width: 90px;
|
||||
background-color: transparent;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new :hover{
|
||||
background-color: rgba(250, 159, 0, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cluster .ant-typography-edit-content{
|
||||
width: 200px;
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Modal, notification, Select, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { withConfigContext } from '../../../../../../../../../../components/ConfigContext';
|
||||
import { handleApiError } from '../../../../../../../../../../services/utils/errorHandler';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
class EditLinks extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.selectedLinks = [];
|
||||
this.state = {
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
loading: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
updateLinks = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({ loading: true });
|
||||
|
||||
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 => {
|
||||
if (res.status === 200) {
|
||||
notification.success({
|
||||
message: 'Saved!',
|
||||
description: 'Links updated successfully!',
|
||||
});
|
||||
|
||||
this.props.updateLinks(this.selectedLinks);
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to update the cluster.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
handleChange = selectedLinks => {
|
||||
this.selectedLinks = selectedLinks;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<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"
|
||||
>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
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>
|
||||
))}
|
||||
</Select>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(EditLinks);
|
@ -1,471 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HomeOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Col,
|
||||
Row,
|
||||
notification,
|
||||
message,
|
||||
Spin,
|
||||
Tag,
|
||||
Divider,
|
||||
Result,
|
||||
} from 'antd';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
|
||||
import axios from 'axios';
|
||||
import Cluster from './components/Cluster';
|
||||
import EditLinks from './components/EditLinks';
|
||||
import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
|
||||
import Authorized from '../../../../../../../../components/Authorized/Authorized';
|
||||
import { isAuthorized } from '../../../../../../../../services/utils/authorizationHandler';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
class Page extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { pageName, pageId } = this.props.match.params;
|
||||
this.pageId = pageId;
|
||||
this.routes = props.routes;
|
||||
this.config = this.props.context;
|
||||
this.pages = [];
|
||||
this.pageNames = {};
|
||||
this.state = {
|
||||
pageName,
|
||||
clusters: [],
|
||||
loading: false,
|
||||
applications: [],
|
||||
isAddNewClusterVisible: false,
|
||||
links: [],
|
||||
};
|
||||
this.hasPermissionToManage = isAuthorized(
|
||||
this.props.context.user,
|
||||
'/permission/admin/device-mgt/enterprise/user/modify',
|
||||
);
|
||||
|
||||
this.hasPermissionToView = isAuthorized(
|
||||
this.props.context.user,
|
||||
'/permission/admin/device-mgt/enterprise/user/view',
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.hasPermissionToView) {
|
||||
this.fetchClusters();
|
||||
this.fetchApplications();
|
||||
this.fetchPages();
|
||||
}
|
||||
}
|
||||
|
||||
removeLoadedCluster = clusterId => {
|
||||
const clusters = [...this.state.clusters];
|
||||
let index = -1;
|
||||
for (let i = 0; i < clusters.length; i++) {
|
||||
if (clusters[i].clusterId === clusterId) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clusters.splice(index, 1);
|
||||
this.setState({
|
||||
clusters,
|
||||
});
|
||||
};
|
||||
|
||||
updatePageName = pageName => {
|
||||
const config = this.props.context;
|
||||
if (pageName !== this.state.pageName && pageName !== '') {
|
||||
const data = {
|
||||
locale: 'en',
|
||||
pageName: pageName,
|
||||
pageId: this.pageId,
|
||||
};
|
||||
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({
|
||||
message: 'Saved!',
|
||||
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}`,
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
handleApiError(
|
||||
error,
|
||||
'Error occurred while trying to save the page name.',
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
swapClusters = (index, swapIndex) => {
|
||||
const clusters = [...this.state.clusters];
|
||||
|
||||
if (swapIndex !== -1 && index < clusters.length) {
|
||||
// swap elements
|
||||
[clusters[index], clusters[swapIndex]] = [
|
||||
clusters[swapIndex],
|
||||
clusters[index],
|
||||
];
|
||||
|
||||
this.setState({
|
||||
clusters,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fetchPages = () => {
|
||||
const config = this.props.context;
|
||||
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 => {
|
||||
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.setState({
|
||||
loading: false,
|
||||
links,
|
||||
});
|
||||
}
|
||||
})
|
||||
.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',
|
||||
duration: 0,
|
||||
description: 'Error occurred while trying to load pages.',
|
||||
});
|
||||
}
|
||||
|
||||
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 => {
|
||||
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));
|
||||
|
||||
this.setState({
|
||||
clusters,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.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)
|
||||
) {
|
||||
// API sends 404 when no apps
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description: 'Error occurred while trying to load clusters.',
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// fetch applications
|
||||
fetchApplications = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({ loading: true });
|
||||
|
||||
const filters = {
|
||||
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 => {
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
applications,
|
||||
});
|
||||
}
|
||||
})
|
||||
.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',
|
||||
duration: 0,
|
||||
description: 'Error occurred while trying to load pages.',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
};
|
||||
|
||||
toggleAddNewClusterVisibility = isAddNewClusterVisible => {
|
||||
this.setState({
|
||||
isAddNewClusterVisible,
|
||||
});
|
||||
};
|
||||
|
||||
addSavedClusterToThePage = cluster => {
|
||||
this.setState({
|
||||
clusters: [...this.state.clusters, cluster],
|
||||
isAddNewClusterVisible: false,
|
||||
});
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
};
|
||||
|
||||
updateLinks = links => {
|
||||
this.setState({
|
||||
links,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
pageName,
|
||||
loading,
|
||||
clusters,
|
||||
applications,
|
||||
isAddNewClusterVisible,
|
||||
links,
|
||||
} = this.state;
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{ paddingTop: 0, backgroundColor: '#fff' }}>
|
||||
<Breadcrumb style={{ paddingBottom: 16 }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps">
|
||||
<HomeOutlined /> Home
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Manage</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<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>*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/enterprise/user/view"
|
||||
yes={
|
||||
<Spin spinning={loading}>
|
||||
<div
|
||||
style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
|
||||
>
|
||||
<Row>
|
||||
<Col md={8} sm={18} xs={24}>
|
||||
{this.hasPermissionToManage && (
|
||||
<Title
|
||||
editable={{ onChange: this.updatePageName }}
|
||||
level={2}
|
||||
>
|
||||
{pageName}
|
||||
</Title>
|
||||
)}
|
||||
{!this.hasPermissionToManage && (
|
||||
<Title level={2}>{pageName}</Title>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Title level={4}>Links</Title>
|
||||
{links.map(link => {
|
||||
if (this.pageNames.hasOwnProperty(link.toString())) {
|
||||
return (
|
||||
<Tag key={link} color="#87d068">
|
||||
{this.pageNames[link.toString()]}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/enterprise/user/modify"
|
||||
yes={
|
||||
<EditLinks
|
||||
updateLinks={this.updateLinks}
|
||||
pageId={this.pageId}
|
||||
selectedLinks={links}
|
||||
pages={this.pages}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
{/* <Col>*/}
|
||||
|
||||
{/* </Col>*/}
|
||||
</Row>
|
||||
|
||||
<Divider dashed={true} />
|
||||
<Title level={4}>Clusters</Title>
|
||||
<Authorized
|
||||
permission="/permission/admin/device-mgt/enterprise/user/modify"
|
||||
yes={
|
||||
<div
|
||||
hidden={isAddNewClusterVisible}
|
||||
style={{ textAlign: 'center' }}
|
||||
>
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="round"
|
||||
icon={<PlusOutlined />}
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.toggleAddNewClusterVisibility(true);
|
||||
}}
|
||||
>
|
||||
Add new cluster
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<div hidden={!isAddNewClusterVisible}>
|
||||
<Cluster
|
||||
cluster={{
|
||||
clusterId: 0,
|
||||
name: 'New Cluster',
|
||||
products: [],
|
||||
}}
|
||||
hasPermissionToManage={true}
|
||||
orderInPage={clusters.length}
|
||||
isTemporary={true}
|
||||
pageId={this.pageId}
|
||||
applications={applications}
|
||||
addSavedClusterToThePage={this.addSavedClusterToThePage}
|
||||
toggleAddNewClusterVisibility={
|
||||
this.toggleAddNewClusterVisibility
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{clusters.map((cluster, index) => {
|
||||
return (
|
||||
<Cluster
|
||||
hasPermissionToManage={this.hasPermissionToManage}
|
||||
key={cluster.clusterId}
|
||||
index={index}
|
||||
orderInPage={cluster.orderInPage}
|
||||
isTemporary={false}
|
||||
cluster={cluster}
|
||||
pageId={this.pageId}
|
||||
applications={applications}
|
||||
swapClusters={this.swapClusters}
|
||||
removeLoadedCluster={this.removeLoadedCluster}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Spin>
|
||||
}
|
||||
no={
|
||||
<Result
|
||||
status="403"
|
||||
title="You don't have permission to view android enterprise configurations."
|
||||
subTitle="Please contact system administrator"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(Page));
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.logo-image {
|
||||
height: 31px;
|
||||
margin: 0 5px 16px 24px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.logo-image img{
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.profile{
|
||||
float:right;
|
||||
margin-right: 2%;
|
||||
}
|
||||
|
||||
.mobile-layout{
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mobile-menu-button{
|
||||
margin-left: 4%;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.bar-icon{
|
||||
margin-top: 10%;
|
||||
font-size: 27px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 660px) {
|
||||
|
||||
.web-layout{
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.mobile-layout{
|
||||
visibility: visible;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.mobile-menu-button{
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
Header{
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dashboard-body{
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-height: 350px) {
|
||||
|
||||
.mobile-menu-button{
|
||||
margin-top: 15px;
|
||||
}
|
||||
.dashboard-body{
|
||||
margin-top: 9%;
|
||||
}
|
||||
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { Form } from '@ant-design/compatible';
|
||||
import '@ant-design/compatible/assets/index.css';
|
||||
import {
|
||||
Typography,
|
||||
Row,
|
||||
Col,
|
||||
Input,
|
||||
Button,
|
||||
message,
|
||||
notification,
|
||||
} from 'antd';
|
||||
import './styles.css';
|
||||
import axios from 'axios';
|
||||
import './styles.css';
|
||||
import { withConfigContext } from '../../components/ConfigContext';
|
||||
|
||||
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="content">
|
||||
<Row>
|
||||
<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={{
|
||||
marginTop: 36,
|
||||
height: 60,
|
||||
}}
|
||||
src={config.theme.logo}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Title level={2}>Login</Title>
|
||||
<WrappedNormalLoginForm />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={4} offset={10}></Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NormalLoginForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inValid: false,
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
const config = this.props.context;
|
||||
const thisForm = this;
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((err, values) => {
|
||||
thisForm.setState({
|
||||
inValid: false,
|
||||
});
|
||||
if (!err) {
|
||||
thisForm.setState({
|
||||
loading: true,
|
||||
});
|
||||
const parameters = {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
platform: 'publisher',
|
||||
};
|
||||
|
||||
const request = Object.keys(parameters)
|
||||
.map(key => key + '=' + parameters[key])
|
||||
.join('&');
|
||||
|
||||
axios
|
||||
.post(window.location.origin + config.serverConfig.loginUri, request)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
let redirectUrl = window.location.origin + '/publisher';
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if (searchParams.has('redirect')) {
|
||||
redirectUrl = searchParams.get('redirect');
|
||||
}
|
||||
window.location = redirectUrl;
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 401
|
||||
) {
|
||||
thisForm.setState({
|
||||
loading: false,
|
||||
inValid: true,
|
||||
});
|
||||
} else {
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 10,
|
||||
description: message,
|
||||
});
|
||||
thisForm.setState({
|
||||
loading: false,
|
||||
inValid: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
let errorMsg = '';
|
||||
if (this.state.inValid) {
|
||||
errorMsg = <Text type="danger">Invalid Login Details</Text>;
|
||||
}
|
||||
let loading = '';
|
||||
if (this.state.loading) {
|
||||
loading = <Text type="secondary">Loading..</Text>;
|
||||
}
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit} className="login-form">
|
||||
<Form.Item>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{ required: true, message: 'Please enter your username' }],
|
||||
})(
|
||||
<Input
|
||||
style={{ height: 32 }}
|
||||
prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
|
||||
placeholder="Username"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{ required: true, message: 'Please enter your password' }],
|
||||
})(
|
||||
<Input
|
||||
style={{ height: 32 }}
|
||||
prefix={<LockOutlined 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"
|
||||
>
|
||||
Log in
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(
|
||||
withConfigContext(NormalLoginForm),
|
||||
);
|
||||
|
||||
export default withConfigContext(Login);
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
@-moz-keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg) scale(1.0);
|
||||
}
|
||||
100% {
|
||||
-moz-transform: rotate(360deg) scale(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg) scale(1.0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg) scale(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg) scale(1.0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg) scale(0.1);
|
||||
transform: rotate(360deg) scale(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.login .background {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 110px;
|
||||
background-size: 100%;
|
||||
animation: spin 200s infinite linear;
|
||||
}
|
||||
|
||||
.login .content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
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}$/,
|
||||
),
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
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',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// 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.',
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export const isAuthorized = (user, permission) => {
|
||||
if (!user || !permission) {
|
||||
return false;
|
||||
}
|
||||
return user.permissions.includes(permission);
|
||||
};
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { notification } from 'antd';
|
||||
|
||||
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}`;
|
||||
// silence 403 forbidden message
|
||||
} else if (
|
||||
!(
|
||||
isForbiddenMessageSilent &&
|
||||
error.hasOwnProperty('response') &&
|
||||
error.response.status === 403
|
||||
)
|
||||
) {
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 10,
|
||||
description: message,
|
||||
});
|
||||
}
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020. Entgra (Pvt) Ltd, https://entgra.io
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Unauthorized copying/redistribution of this file, via any medium
|
||||
* is strictly prohibited.
|
||||
* Proprietary and confidential.
|
||||
*
|
||||
* Licensed under the Entgra Commercial License,
|
||||
* Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
* https://entgra.io/licenses/entgra-commercial/1.0
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { notification } from 'antd';
|
||||
|
||||
export const getUiConfig = config => {
|
||||
return axios
|
||||
.get(window.location.origin + config.serverConfig.appUiConfigUri)
|
||||
.then(res => {
|
||||
return res.data;
|
||||
})
|
||||
.catch(error => {
|
||||
notification.error({
|
||||
message: 'There was a problem',
|
||||
duration: 0,
|
||||
description: 'Error occurred while trying to load UI configurations.',
|
||||
});
|
||||
});
|
||||
};
|
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
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',
|
||||
output: {
|
||||
publicPath: '/publisher/',
|
||||
},
|
||||
watch: false,
|
||||
resolve: {
|
||||
alias: {
|
||||
AppData: path.resolve(__dirname, 'source/src/app/common/'),
|
||||
AppComponents: path.resolve(__dirname, 'source/src/app/components/'),
|
||||
},
|
||||
extensions: ['.jsx', '.js', '.ttf', '.woff', '.woff2', '.svg'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'html-loader',
|
||||
options: { minimize: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
'css-loader',
|
||||
'postcss-loader',
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: ['style-loader', 'scss-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
},
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: {
|
||||
modifyVars: {
|
||||
'primary-color': configurations.theme.primaryColor,
|
||||
'link-color': configurations.theme.primaryColor,
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|svg)$/,
|
||||
loader: 'url-loader?limit=100000',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g)/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
name: './img/[name].[ext]',
|
||||
limit: 10000,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'img-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebPackPlugin({
|
||||
template: './src/index.html',
|
||||
filename: './index.html',
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
Config: JSON.stringify(require('./public/conf/config.json')),
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
config.watch = true;
|
||||
}
|
||||
|
||||
module.exports = config;
|
@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
~
|
||||
~ Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
|
||||
<display-name>Publisher-Webapp</display-name>
|
||||
<error-page>
|
||||
<error-code>404</error-code>
|
||||
<location>/index.html</location>
|
||||
</error-page>
|
||||
</web-app>
|
@ -1,171 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
~
|
||||
~ Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||
<artifactId>application-mgt</artifactId>
|
||||
<version>4.1.11-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>org.wso2.carbon.device.application.mgt.store.ui</artifactId>
|
||||
<version>4.1.11-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>WSO2 Carbon - Application Management Store UI Component</name>
|
||||
<url>https://entgra.io</url>
|
||||
<description>This Component contains Application Management store UI</description>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<packagingExcludes>WEB-INF/lib/*cxf*.jar</packagingExcludes>
|
||||
<warName>store</warName>
|
||||
<webResources>
|
||||
<resource>
|
||||
<directory>${npm.output.directory}/dist</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${npm.output.directory}/public</directory>
|
||||
<targetPath>public</targetPath>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>${frontend.mave.version}</version>
|
||||
<configuration>
|
||||
<workingDirectory>${npm.working.dir}</workingDirectory>
|
||||
<!-- where to install npm -->
|
||||
<installDirectory>${npm.install.dir}</installDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<nodeVersion>${node.version}</nodeVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<!-- Optional configuration which provides for running any npm command -->
|
||||
<configuration>
|
||||
<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>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run-script ${npm.build.command}</arguments>
|
||||
</configuration>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>org.codehaus.mojo</groupId>-->
|
||||
<!-- <artifactId>exec-maven-plugin</artifactId>-->
|
||||
<!-- <version>1.5.0</version>-->
|
||||
<!-- <executions>-->
|
||||
<!-- <execution>-->
|
||||
<!-- <id>npm install (initialize)</id>-->
|
||||
<!-- <goals>-->
|
||||
<!-- <goal>exec</goal>-->
|
||||
<!-- </goals>-->
|
||||
<!-- <phase>initialize</phase>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <workingDirectory>react-app</workingDirectory>-->
|
||||
<!-- <executable>${npm.executable}</executable>-->
|
||||
<!-- <arguments>-->
|
||||
<!-- <argument>install</argument>-->
|
||||
<!-- <argument>--silent</argument>-->
|
||||
<!-- </arguments>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </execution>-->
|
||||
<!-- <execution>-->
|
||||
<!-- <id>npm run build (compile)</id>-->
|
||||
<!-- <goals>-->
|
||||
<!-- <goal>exec</goal>-->
|
||||
<!-- </goals>-->
|
||||
<!-- <phase>compile</phase>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <workingDirectory>react-app</workingDirectory>-->
|
||||
<!-- <executable>${npm.executable}</executable>-->
|
||||
<!-- <arguments>-->
|
||||
<!-- <argument>run</argument>-->
|
||||
<!-- <argument>${npm.build.command}</argument>-->
|
||||
<!-- </arguments>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </execution>-->
|
||||
<!-- </executions>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <workingDirectory>${npm.working.dir}</workingDirectory>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </plugin>-->
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>platform-windows</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>windows</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<!-- Override the executable names for Windows -->
|
||||
<npm.executable>npm.cmd</npm.executable>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
<properties>
|
||||
<maven.test.skip>false</maven.test.skip>
|
||||
<npm.executable>npm</npm.executable>
|
||||
<npm.build.command>build_prod</npm.build.command>
|
||||
<npm.working.dir>./react-app</npm.working.dir>
|
||||
<npm.install.dir>./react-app/tmp</npm.install.dir>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<npm.output.directory>react-app</npm.output.directory>
|
||||
</properties>
|
||||
</project>
|
@ -1,325 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"parser": "flow"
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
module.exports = function(api) {
|
||||
api.cache(true);
|
||||
const presets = ['@babel/preset-env', '@babel/preset-react'];
|
||||
const plugins = ['@babel/plugin-proposal-class-properties'];
|
||||
|
||||
return {
|
||||
presets,
|
||||
plugins,
|
||||
};
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue