diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/App.css b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/App.css
index adbde74ba53..048a7eb7ab4 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/App.css
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/App.css
@@ -8,7 +8,7 @@
.release .release-icon img{
width: 100%;
- border-radius: 100%;
+ border-radius: 28%;
}
.release .release-title{
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js
index 412589062b2..effa0d7ec67 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -1,44 +1,63 @@
import React from "react";
-import {Avatar, Row, Col, Typography, Button} from "antd";
+import {Divider, Row, Col, Typography, Button} from "antd";
import "../../../App.css";
+import config from "../../../../public/conf/config.json";
-const {Title, Text} = Typography;
+const {Title, Text, Paragraph} = Typography;
class ReleaseView extends React.Component {
render() {
const release = this.props.release;
console.log(release);
return (
-
-
-
-
-
-
- App Name
- {release.version}
- {release.description}
-
-
-
-
- Open in store
- edit
-
-
-
-
-
-
-
- {release.screenshots.map((screenshotUrl)=>{
- return (
-
-
-
- )
- })}
-
+
+
+
+
+
+
+
+ App Name
+ {release.version}
+ {release.description}
+
+
+
+
+ edit
+ {
+ window.open("https://"+ config.serverConfig.hostname + ':' + config.serverConfig.httpsPort+"/store/apps/"+release.uuid)
+ }}>
+ Open in store
+
+
+
+
+
+
+
+ {release.screenshots.map((screenshotUrl) => {
+ return (
+
+
+
+ )
+ })}
+
+
+
+ Ant Design, a design language for background applications, is refined by Ant UED Team. Ant
+ Design, a design language for background applications, is refined by Ant UED Team. Ant Design,
+ a design language for background applications, is refined by Ant UED Team. Ant Design, a
+ design language for background applications, is refined by Ant UED Team. Ant Design, a design
+ language for background applications, is refined by Ant UED Team. Ant Design, a design
+ language for background applications, is refined by Ant UED Team.
+
+
);
}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/.env b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/.env
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/package.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/package.json
index af55bd6b6bd..5075b4fe035 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/package.json
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/package.json
@@ -1,7 +1,7 @@
{
"name": "store",
"version": "1.0.0",
- "description": "WSO2 IoT Server App Publisher",
+ "description": "WSO2 IoT Server App Store",
"main": "App.js",
"proxy": "http://localhost:3001",
"repository": {
@@ -12,11 +12,23 @@
"dependencies": {
"acorn": "^6.1.1",
"antd": "^3.15.0",
+ "axios": "^0.18.0",
+ "d3": "^5.9.2",
+ "dagre": "^0.8.4",
+ "keymirror": "^0.1.1",
+ "rc-viewer": "0.0.9",
"react": "^16.8.4",
+ "react-d3-graph": "^2.0.2",
"react-dom": "^16.8.4",
+ "react-highlight-words": "^0.16.0",
+ "react-image-viewer-zoom": "^1.0.36",
+ "react-router": "latest",
"react-router-config": "^5.0.0",
"react-router-dom": "latest",
- "react-scripts": "2.1.8"
+ "react-scripts": "2.1.8",
+ "react-star-ratings": "^2.3.0",
+ "redux-thunk": "^2.3.0",
+ "storm-react-diagrams": "^5.2.1"
},
"devDependencies": {
"@babel/core": "^7.0.0",
@@ -34,6 +46,7 @@
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"img-loader": "^3.0.1",
+ "json-loader": "^0.5.7",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^0.5.0",
@@ -48,6 +61,7 @@
"react": "^15.6.2",
"react-dom": "^15.6.2",
"react-intl": "^2.4.0",
+ "react-redux": "^7.0.2",
"redux": "^4.0.1",
"sass-loader": "^6.0.7",
"style-loader": "^0.18.2",
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/config.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/config.json
index 63678cb5418..c2e66dda140 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/config.json
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/config.json
@@ -3,10 +3,16 @@
"type": "default",
"value": "lightBaseTheme"
},
- "config": {
+ "serverConfig": {
"hostname": "localhost",
"httpsPort": "9443",
- "apiPort": "8243"
+ "invokerUri": "/api/application-mgt-handler/v1.0/invoke",
+ "loginUri": "/api/application-mgt-handler/v1.0/login"
},
- "serverUrl" : "https://localhost:9443"
+ "serverUrl" : "https://localhost:9443",
+ "defaultPlatformIcons": {
+ "default": "http://www.newdesignfile.com/postpic/2015/08/square-app-icon-blue_77131.png",
+ "android": "data:image/png;base64,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",
+ "ios" : "data:image/png;base64,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"
+ }
}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/manifest.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/manifest.json
index 2a700bb50e7..60f712111e9 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/manifest.json
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/conf/manifest.json
@@ -1,6 +1,6 @@
{
- "short_name": "App Publisher",
- "name": "WSO2 IoT App Publisher",
+ "short_name": "App Store",
+ "name": "WSO2 IoT App Store",
"icons": [
{
"src": "images/favicon.png",
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/themes/default/default-theme.css b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/themes/default/default-theme.css
index 32cb3c22b99..d6e82a67450 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/themes/default/default-theme.css
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/public/themes/default/default-theme.css
@@ -328,8 +328,8 @@ body {
margin-top: 128px !important;
}
-/* Holds the app publisher pages. */
-.publisher-card {
+/* Holds the app pages. */
+.store-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);
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/server/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/server/index.js
deleted file mode 100644
index 1f8cf6c4d7c..00000000000
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/server/index.js
+++ /dev/null
@@ -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')
-);
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.css b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.css
index b41d297cab1..1e8064d0d9b 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.css
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.css
@@ -1,33 +1,45 @@
-.App {
- text-align: center;
+.ant-upload.ant-upload-drag {
+ height: 170px;
}
-.App-logo {
- animation: App-logo-spin infinite 20s linear;
- height: 40vmin;
- pointer-events: none;
+.release-icon img{
+ width: 100%;
+ border-radius: 28%;
+}
+.release .release-icon{
+ margin-right: 15px;
+}
+.appCard .release-icon{
+ margin-bottom: 10px;
+}
+.release .release-title{
+ margin-left: 15px;
}
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
+.release .release-screenshot img{
+ width: 100%;
+ border-radius: 15px;
+ padding: 5px;
}
-.App-link {
- color: #61dafb;
+.logo {
+ width: 120px;
+ height: 31px;
+ margin: 0 0 16px 20px;
+ float: left;
}
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
+.logo img{
+ height: 35px;
}
+
+.main-container{
+ background: #f0f2f5;
+ min-height: 780px
+}
+
+@media only screen and (min-width: 768px) {
+ .main-container{
+ padding: 24px;
+ }
+}
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.js
index 5cf9be115e3..c730379bdb3 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/App.js
@@ -1,22 +1,35 @@
import React from "react";
import "antd/dist/antd.css";
-import { renderRoutes } from "react-router-config";
+import RouteWithSubRoutes from "./components/RouteWithSubRoutes";
+import {
+ BrowserRouter as Router,
+ Link, Redirect, Switch,
+} from 'react-router-dom';
class App extends React.Component {
+ routes;
+
constructor(props) {
super(props);
- this.state = {
- route : props.route
- }
+ this.routes = props.routes;
}
- render() {
- return (
-
- {renderRoutes(this.state.route.routes)}
-
- );
- }
+ render() {
+ console.log(this.routes);
+ return (
+
+
+
+
+ {this.routes.map((route) => (
+
+ ))}
+
+
+
+
+ );
+ }
}
export default App;
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/RouteWithSubRoutes.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/RouteWithSubRoutes.js
new file mode 100644
index 00000000000..41cb84caf83
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/RouteWithSubRoutes.js
@@ -0,0 +1,19 @@
+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(
+
(
+
+ )}/>
+ );
+ }
+
+}
+
+export default RouteWithSubRoutes;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js
new file mode 100644
index 00000000000..c77feb13a5d
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js
@@ -0,0 +1,71 @@
+import {
+ Skeleton, Switch, Card, Icon, Avatar, Typography, Col, Row, Rate
+} from 'antd';
+import React from "react";
+import {openReleasesModal} from "../../js/actions";
+import {connect} from "react-redux";
+import {Link} from "react-router-dom";
+import "../../App.css";
+import StarRatings from 'react-star-ratings';
+
+const {Meta} = Card;
+const {Text, Title} = Typography;
+
+const mapDispatchToProps = dispatch => ({
+ openReleasesModal: (app) => dispatch(openReleasesModal(app))
+});
+
+class ConnectedAppCard extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.handleReleasesClick = this.handleReleasesClick.bind(this);
+ }
+
+ handleReleasesClick() {
+ this.props.openReleasesModal(this.props.app);
+ }
+
+ render() {
+ const app = this.props.app;
+ const release = this.props.app.applicationReleases[0];
+ console.log(this.props);
+
+ const description = (
+
+
+
+
+
+ {/* */}
+
+
+ {app.name}
+ {app.deviceType}
+
+
+
+
+
+ );
+
+ return (
+
+
+
+ );
+ }
+}
+
+const AppCard = connect(null, mapDispatchToProps)(ConnectedAppCard);
+
+export default AppCard;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js
new file mode 100644
index 00000000000..e41c30290ab
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js
@@ -0,0 +1,38 @@
+import React from "react";
+import AppCard from "./AppCard";
+import {Col, Row} from "antd";
+import {connect} from "react-redux";
+import {getApps} from "../../js/actions";
+
+// connecting state.apps with the component
+const mapStateToProps= state => {
+ return {apps : state.apps}
+};
+
+
+class ConnectedAppList extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ componentDidMount() {
+ this.props.getApps();
+ }
+
+ render() {
+ return (
+
+ {this.props.apps.map(app => (
+
+
+
+ ))}
+
+ );
+ }
+}
+
+const AppList = connect(mapStateToProps,{getApps})(ConnectedAppList);
+
+export default AppList;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/ReleaseModal.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/ReleaseModal.js
new file mode 100644
index 00000000000..2265aa6d3f9
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/ReleaseModal.js
@@ -0,0 +1,85 @@
+import React from "react";
+import {Modal, Typography,List, Avatar} from 'antd';
+import {connect} from 'react-redux';
+import {Link} from "react-router-dom";
+
+// connecting state.releaseView with the component
+const mapStateToProps = state => {
+ return {releaseView: state.releaseView}
+};
+
+const Text = Typography;
+
+class ConnectedReleaseModal extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ visible: false,
+ app: null
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps !== this.props) {
+ this.setState({
+ visible: nextProps.releaseView.visible,
+ app: nextProps.releaseView.app
+ })
+ }
+ }
+
+ showModal = () => {
+ this.setState({
+ visible: true,
+ });
+ };
+
+ handleOk = (e) => {
+ this.setState({
+ visible: false,
+ });
+ };
+
+ handleCancel = (e) => {
+ this.setState({
+ visible: false,
+ });
+ };
+
+ render() {
+ if (this.props.releaseView.app != null) {
+ const app = this.props.releaseView.app;
+ return (
+
+
+ Some contents...
+ (
+
+ }
+ title={ {release.version}}
+ description={release.description}
+ />
+
+ )}
+ />
+
+
+ );
+ } else {
+ return null;
+ }
+ }
+}
+
+const ReleaseModal = connect(mapStateToProps, null)(ConnectedReleaseModal);
+
+export default ReleaseModal;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.css b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.css
new file mode 100644
index 00000000000..9e7f3bc5794
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.css
@@ -0,0 +1,72 @@
+.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%;
+}
+
+.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;
+}
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.js
new file mode 100644
index 00000000000..6430d740e2f
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.js
@@ -0,0 +1,91 @@
+import React from "react";
+import {Row, Typography, Icon} from "antd";
+import StarRatings from "react-star-ratings";
+import "./DetailedRating.css";
+import {connect} from "react-redux";
+import {getDetailedRating} from "../../../js/actions";
+
+const { Text } = Typography;
+
+// connecting state. with the component
+const mapStateToProps= state => {
+ return {detailedRating : state.detailedRating}
+};
+
+const mapDispatchToProps = dispatch => ({
+ getDetailedRating: (uuid) => dispatch(getDetailedRating(uuid))
+});
+
+
+
+class ConnectedDetailedRating extends React.Component{
+
+ componentDidMount() {
+ this.props.getDetailedRating(this.props.uuid);
+ }
+
+ render() {
+ const detailedRating = this.props.detailedRating;
+
+ if(detailedRating ==null){
+ return null;
+ }
+
+ const totalCount = detailedRating.noOfUsers;
+ const ratingVariety = detailedRating.ratingVariety;
+
+ const ratingArray = [];
+
+ for (let [key, value] of Object.entries(ratingVariety)) {
+ ratingArray.push(value);
+ }
+
+ const maximumRating = Math.max(...ratingArray);
+
+ console.log(maximumRating);
+
+ return (
+
+
+
{detailedRating.ratingValue.toFixed(1)}
+
+
+
{totalCount} total
+
+
+
+ 5
+
+
+
+ 4
+
+
+
+ 3
+
+
+
+ 2
+
+
+
+ 1
+
+
+
+
+ );
+ }
+}
+
+const DetailedRating = connect(mapStateToProps,mapDispatchToProps)(ConnectedDetailedRating);
+
+export default DetailedRating;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
new file mode 100644
index 00000000000..3a87ac38b81
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -0,0 +1,61 @@
+import React from "react";
+import {Divider, Row, Col, Typography, Button, Rate} from "antd";
+import "../../../App.css";
+import ImgViewer from "../../apps/release/images/ImgViewer";
+import StarRatings from "react-star-ratings";
+import DetailedRating from "./DetailedRating";
+const {Title, Text, Paragraph} = Typography;
+
+class ReleaseView extends React.Component {
+ render() {
+ const release = this.props.release;
+ return (
+
+
+
+
+
+
+
+ App Name
+ Version : {release.version}
+
+
+
+
+
+ Enterprise Install
+ Install
+
+
+
+
+
+
+
+
+
+
+ {release.description}
+
+
+
REVIEWS
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default ReleaseView;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/images/ImgViewer.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/images/ImgViewer.js
new file mode 100644
index 00000000000..447531aa9a9
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/images/ImgViewer.js
@@ -0,0 +1,45 @@
+import React, {Component} from 'react';
+import RcViewer from 'rc-viewer';
+import {Col} from "antd";
+
+class ImgViewer extends Component {
+ render() {
+ const options = {
+ title: false,
+ toolbar: {
+ zoomIn: 0,
+ zoomOut: 0,
+ oneToOne: 0,
+ reset: 0,
+ prev: 1,
+ play: {
+ show: 0
+ },
+ next: 1,
+ rotateLeft: 0,
+ rotateRight: 0,
+ flipHorizontal: 0,
+ flipVertical: 0
+ },
+ rotatable: false,
+ transition: false,
+ movable : false
+ };
+ return (
+
+
+ {this.props.images.map((screenshotUrl) => {
+ return (
+
+
+
+ )
+ })}
+
+
+ );
+
+ }
+}
+
+export default ImgViewer;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.css b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.css
index 8b45ed5931f..fa1cc5934e2 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.css
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.css
@@ -1,3 +1,27 @@
.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;
}
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.html b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.html
index 98499e9ecfe..d469799e308 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.html
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.html
@@ -1 +1,8 @@
-
\ No newline at end of file
+
+
+
+
+ Entgra App Store
+
+
+
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.js
index 96931fc788c..bf001fa2056 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/index.js
@@ -1,41 +1,53 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import './index.css';
-import App from './App';
import * as serviceWorker from './serviceWorker';
-import { renderRoutes } from "react-router-config";
-import Dashboard from "./pages/dashboard/Dashboard"
+import App from "./App";
import Login from "./pages/Login";
-import {BrowserRouter} from "react-router-dom";
+import Dashboard from "./pages/dashboard/Dashboard";
+import Apps from "./pages/dashboard/apps/Apps";
+import Release from "./pages/dashboard/apps/release/Release";
+import AddNewApp from "./pages/dashboard/add-new-app/AddNewApp";
+import './index.css';
+import store from "./js/store/index";
+import {Provider} from "react-redux";
const routes = [
{
- component: App,
+ path: '/store/login',
+ exact: true,
+ component: Login
+ },
+ {
+ path: '/store/apps',
+ exact: false,
+ component: Dashboard,
routes: [
{
- path: "/publisher",
- exact: true,
- component: Dashboard,
- routes: [
- {
- path: "/publisher/a",
- component: Login
- }
- ]
+ path: '/store/apps',
+ component: Apps,
+ exact: true
},
{
- path: "/publisher/login",
- component: Login
+ path: '/store/apps/new-app',
+ component: AddNewApp,
+ exact: true
+ },
+ {
+ path: '/store/apps/:uuid',
+ exact: true,
+ component: Release
}
]
}
];
-ReactDOM.render(
- {/* kick it all off with the root route */}
- {renderRoutes(routes)}
- , document.getElementById('root'));
+
+ReactDOM.render(
+
+
+ ,
+ 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.
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/actions/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/actions/index.js
new file mode 100644
index 00000000000..4fdcd14c1b1
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/actions/index.js
@@ -0,0 +1,168 @@
+import axios from "axios";
+import ActionTypes from "../constants/ActionTypes";
+import config from "../../../public/conf/config.json";
+
+export const getApps = () => dispatch => {
+
+ const request = "method=post&content-type=application/json&payload={}&api-endpoint=/application-mgt-store/v1.0/applications";
+
+ return axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
+ ).then(res => {
+ if (res.status === 200) {
+ let apps = [];
+
+ if (res.data.data.hasOwnProperty("applications")) {
+ apps = res.data.data.applications;
+ }
+ dispatch({type: ActionTypes.GET_APPS, payload: apps});
+ }
+
+ }).catch(function (error) {
+ if (error.response.status === 401) {
+ window.location.href = 'https://localhost:9443/store/login';
+ }
+ });
+
+};
+
+export const getRelease = (uuid) => dispatch => {
+
+ const request = "method=get&content-type=application/json&payload={}&api-endpoint=/application-mgt-store/v1.0/applications/" + uuid;
+
+ return axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
+ ).then(res => {
+ if (res.status === 200) {
+ let release = res.data.data.applicationReleases[0];
+ dispatch({
+ type: ActionTypes.GET_RELEASE,
+ payload: {
+ release: release,
+ releaseLoading: false
+ }
+ });
+ }
+ }).catch(function (error) {
+ if (error.response.status === 401) {
+ window.location.href = 'https://localhost:9443/store/login';
+ }else if(error.response.status===404){
+ dispatch({
+ type: ActionTypes.GET_RELEASE,
+ payload: {
+ release: null,
+ releaseLoading: false
+ }});
+ }
+ });
+
+
+};
+
+export const openReleasesModal = (app) => dispatch => {
+ dispatch({
+ type: ActionTypes.OPEN_RELEASES_MODAL,
+ payload: {
+ app: app
+ }
+ });
+};
+
+
+export const openLifecycleModal = (nextState) => dispatch => {
+ dispatch({
+ type: ActionTypes.OPEN_LIFECYCLE_MODAL,
+ payload: {
+ nextState: nextState
+ }
+ });
+};
+
+export const closeLifecycleModal = () => dispatch => {
+ dispatch({
+ type: ActionTypes.CLOSE_LIFECYCLE_MODAL
+ });
+};
+
+export const setLoading = (stateToLoad) => dispatch => {
+ dispatch({
+ type: ActionTypes.SET_LOADING_STATE,
+ payload: {
+ stateToLoad: stateToLoad
+ }
+ });
+};
+
+export const getLifecycle = () => dispatch => {
+ const request = "method=get&content-type=application/json&payload={}&api-endpoint=/application-mgt-store/v1.0/applications/lifecycle-config";
+
+ return axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
+ ).then(res => {
+ if (res.status === 200) {
+ let lifecycle = res.data.data;
+ dispatch({type: ActionTypes.GET_LIFECYCLE, payload: lifecycle});
+ }
+
+ }).catch(function (error) {
+ if (error.response.status === 401) {
+ window.location.href = 'https://localhost:9443/store/login';
+ }
+ });
+};
+
+export const updateLifecycleState = (uuid, nextState, reason) => dispatch => {
+
+ const payload = {
+ action: nextState,
+ reason: reason
+ };
+
+ const request = "method=post&content-type=application/json&payload=" + JSON.stringify(payload) + "&api-endpoint=/application-mgt-store/v1.0/applications/life-cycle/" + uuid;
+
+
+ return axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
+ ).then(res => {
+ if (res.status === 201) {
+ let release = res.data.data;
+ dispatch({type: ActionTypes.UPDATE_LIFECYCLE_STATE, payload: release});
+ }else {
+ alert("error");
+ dispatch({
+ type: ActionTypes.CLOSE_LIFECYCLE_MODAL
+ });
+ }
+
+ }).catch(function (error) {
+ if (error.response.status === 401) {
+ window.location.href = 'https://localhost:9443/store/login';
+ } else if (error.response.status === 500) {
+ alert("error");
+ dispatch({
+ type: ActionTypes.CLOSE_LIFECYCLE_MODAL
+ });
+ }
+ });
+
+
+};
+
+export const getDetailedRating = (uuid) => dispatch => {
+ const request = "method=get&content-type=application/json&payload={}&api-endpoint=/application-mgt-store/v1.0/reviews/"+uuid+"/rating";
+
+ return axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
+ ).then(res => {
+ if (res.status === 200) {
+ let detailedRating = res.data.data;
+ dispatch({type: ActionTypes.GET_DETAILED_RATING, payload: detailedRating});
+ }
+
+ }).catch(function (error) {
+ if (error.response.status === 401) {
+ window.location.href = 'https://localhost:9443/store/login';
+ } else{
+ dispatch({
+ type: ActionTypes.GET_DETAILED_RATING, payload: null
+ });
+ }
+ });
+
+
+};
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/constants/ActionTypes.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/constants/ActionTypes.js
new file mode 100644
index 00000000000..bd47f220740
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/constants/ActionTypes.js
@@ -0,0 +1,17 @@
+import keyMirror from 'keymirror';
+
+const ActionTypes = keyMirror({
+ LOGIN: null,
+ GET_APPS: null,
+ OPEN_RELEASES_MODAL: null,
+ CLOSE_RELEASES_MODAL: null,
+ GET_RELEASE: null,
+ GET_LIFECYCLE: null,
+ OPEN_LIFECYCLE_MODAL: null,
+ CLOSE_LIFECYCLE_MODAL: null,
+ UPDATE_LIFECYCLE_STATE: null,
+ SET_LOADING_STATE: null,
+ GET_DETAILED_RATING: null
+});
+
+export default ActionTypes;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/reducers/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/reducers/index.js
new file mode 100644
index 00000000000..fcf1aa7db64
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/reducers/index.js
@@ -0,0 +1,83 @@
+import ActionTypes from "../constants/ActionTypes";
+
+const initialState = {
+ apps: [],
+ releaseView: {
+ visible: false,
+ app: null
+ },
+ release: null,
+ lifecycle: null,
+ lifecycleModal: {
+ visible: false,
+ nextState: null
+ },
+ loadingState: {
+ release: true
+ },
+ detailedRating: null
+
+};
+
+function rootReducer(state = initialState, action) {
+ if (action.type === ActionTypes.GET_APPS) {
+ return Object.assign({}, state, {
+ apps: action.payload
+ });
+ } else if (action.type === ActionTypes.OPEN_RELEASES_MODAL) {
+ return Object.assign({}, state, {
+ releaseView: {
+ visible: true,
+ app: action.payload.app
+ }
+ });
+ } else if (action.type === ActionTypes.GET_RELEASE) {
+ let loadingState = {...state.loadingState};
+ loadingState.release = action.payload.releaseLoading;
+ return Object.assign({}, state, {
+ release: action.payload.release,
+ loadingState: loadingState
+ });
+ } else if (action.type === ActionTypes.GET_LIFECYCLE) {
+ return Object.assign({}, state, {
+ lifecycle: action.payload
+ });
+ } else if (action.type === ActionTypes.OPEN_LIFECYCLE_MODAL) {
+ return Object.assign({}, state, {
+ lifecycleModal: {
+ visible: true,
+ nextState: action.payload.nextState
+ }
+ });
+ } else if (action.type === ActionTypes.CLOSE_LIFECYCLE_MODAL) {
+ return Object.assign({}, state, {
+ lifecycleModal: {
+ visible: false,
+ nextState: null
+ }
+ });
+ } else if (action.type === ActionTypes.UPDATE_LIFECYCLE_STATE) {
+ return Object.assign({}, state, {
+ lifecycleModal: {
+ visible: false,
+ nextState: null,
+ },
+ release: action.payload
+ });
+ } else if (action.type === ActionTypes.SET_LOADING_STATE) {
+ let loadingState = {...state.loadingState};
+ loadingState[action.payload.stateToLoad] = true;
+ return Object.assign({}, state, {
+ loadingState: loadingState
+ });
+ } else if (action.type === ActionTypes.GET_DETAILED_RATING) {
+ return Object.assign({}, state, {
+ detailedRating: action.payload
+ });
+ }
+
+ return state;
+}
+
+
+export default rootReducer;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/store/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/store/index.js
new file mode 100644
index 00000000000..04957eb39a8
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/store/index.js
@@ -0,0 +1,5 @@
+import { createStore, applyMiddleware } from "redux";
+import rootReducer from "../reducers/index";
+import thunk from "redux-thunk";
+const store = createStore(rootReducer, applyMiddleware(thunk));
+export default store;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/utils/dagre-utils.ts b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/utils/dagre-utils.ts
new file mode 100644
index 00000000000..58a006f9a93
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/utils/dagre-utils.ts
@@ -0,0 +1,56 @@
+import * as dagre from "dagre";
+import * as _ from "lodash";
+
+const size = {
+ width: 60,
+ height: 60
+};
+
+export function distributeElements(model) {
+ let clonedModel = _.cloneDeep(model);
+ let nodes = distributeGraph(clonedModel);
+ nodes.forEach(node => {
+ let modelNode = clonedModel.nodes.find(item => item.id === node.id);
+ modelNode.x = node.x - node.width / 2;
+ modelNode.y = node.y - node.height / 2;
+ });
+ return clonedModel;
+}
+
+function distributeGraph(model) {
+ let nodes = mapElements(model);
+ let edges = mapEdges(model);
+ let graph = new dagre.graphlib.Graph();
+ graph.setGraph({});
+ graph.setDefaultEdgeLabel(() => ({}));
+ //add elements to dagre graph
+ nodes.forEach(node => {
+ graph.setNode(node.id, node.metadata);
+ });
+ edges.forEach(edge => {
+ if (edge.from && edge.to) {
+ graph.setEdge(edge.from, edge.to);
+ }
+ });
+ //auto-distribute
+ dagre.layout(graph);
+ return graph.nodes().map(node => graph.node(node));
+}
+
+function mapElements(model) {
+ // dagre compatible format
+ return model.nodes.map(node => ({ id: node.id, metadata: { ...size, id: node.id } }));
+}
+
+function mapEdges(model) {
+ // returns links which connects nodes
+ // we check are there both from and to nodes in the model. Sometimes links can be detached
+ return model.links
+ .map(link => ({
+ from: link.source,
+ to: link.target
+ }))
+ .filter(
+ item => model.nodes.find(node => node.id === item.from) && model.nodes.find(node => node.id === item.to)
+ );
+}
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/Login.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/Login.js
index f4faa2a3489..38cb11a1c58 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/Login.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/Login.js
@@ -1,7 +1,11 @@
import React from "react";
-import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox,} from 'antd';
+import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox} from 'antd';
import styles from './Login.less';
+import axios from 'axios';
+import config from "../../public/conf/config.json";
+
const {Title} = Typography;
+const {Text} = Typography;
class Login extends React.Component {
render() {
@@ -33,33 +37,77 @@ class Login extends React.Component {
}
class NormalLoginForm extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ inValid: false,
+ loading : false
+ };
+ }
+
handleSubmit = (e) => {
+ const thisForm = this;
e.preventDefault();
this.props.form.validateFields((err, values) => {
+ thisForm.setState({
+ inValid: false
+ });
if (!err) {
+ thisForm.setState({
+ loading: true
+ });
console.log('Received values of form: ', values);
+ let data = "username=" + values.username + "&password=" + values.password + "&platform=store";
+ axios.post('https://'+config.serverConfig.hostname+':'+config.serverConfig.httpsPort+config.serverConfig.loginUri, data
+ ).then(res => {
+ if (res.status === 200) {
+ window.location = res.data.url;
+ }
+ }).catch(function (error) {
+ if (error.response.status === 400) {
+ thisForm.setState({
+ inValid: true,
+ loading: false
+ });
+ }
+ });
}
+
});
- }
+ };
render() {
- const { getFieldDecorator } = this.props.form;
+ const {getFieldDecorator} = this.props.form;
+ let errorMsg = "";
+ if (this.state.inValid) {
+ errorMsg = Invalid Login Details ;
+ }
+ let loading = "";
+ if (this.state.loading) {
+ loading = Loading.. ;
+ }
return (
- {getFieldDecorator('userName', {
- rules: [{ required: true, message: 'Please input your username!' }],
+ {getFieldDecorator('username', {
+ rules: [{required: true, message: 'Please input your username!'}],
})(
- } placeholder="Username" />
+ }
+ placeholder="Username"/>
)}
{getFieldDecorator('password', {
- rules: [{ required: true, message: 'Please input your Password!' }],
+ rules: [{required: true, message: 'Please input your Password!'}],
})(
- } type="password" placeholder="Password" />
+ } type="password"
+ placeholder="Password"/>
)}
+ {loading}
+ {errorMsg}
{getFieldDecorator('remember', {
valuePropName: 'checked',
@@ -67,17 +115,17 @@ class NormalLoginForm extends React.Component {
})(
Remember me
)}
+
Forgot password
Log in
- Or register now!
);
}
}
-const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm);
+const WrappedNormalLoginForm = Form.create({name: 'normal_login'})(NormalLoginForm);
export default Login;
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js
index 6088af4b6f3..da92c72a2d0 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js
@@ -1,58 +1,60 @@
import React from "react";
-import { Layout, Menu, Breadcrumb } from 'antd';
+import {Layout, Menu, Icon} from 'antd';
-const { Header, Content, Footer } = Layout;
+const {Header, Content, Footer} = Layout;
-import styles from './Dashboard.less';
import Logo from "../../../public/images/logo.svg";
-import Login from "../Login";
-import {renderRoutes} from "react-router-config";
-import {NavLink} from "react-router-dom";
-
+import {Link, NavLink} from "react-router-dom";
+import RouteWithSubRoutes from "../../components/RouteWithSubRoutes"
+import {Switch, Redirect} from 'react-router'
+import "../../App.css";
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
- route : props.route
+ routes: props.routes
}
- console.log(props);
}
render() {
return (
-
-
-
-
- nav 1
- nav 2
- nav 3
-
-
-
-
- Home
- List
- App
-
-
- Items
-
+
+
+
+
+
+
+
+ Apps
+ Apps
+ Add New
+ App
+
+
+
+
+
+
+
+ {this.state.routes.map((route) => (
+
+ ))}
+
+
- {/* child routes won't render without this */}
- {renderRoutes(this.state.route.routes, { someProp: "these extra props are optional" })}
- Content
-
-
-
+
+
+
+
);
}
}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.less b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.less
index 483f13e1170..dc7904b51b3 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.less
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.less
@@ -1,7 +1,15 @@
.logo {
width: 120px;
height: 31px;
- background: rgba(0,0,0,.2);
- margin: 16px 24px 16px 0;
+ margin: 16px 0 16px 20px;
float: left;
+
+ img{
+ height: 35px;
+ }
+}
+
+
+input{
+ min-height: 0;
}
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/AddNewApp.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/AddNewApp.js
new file mode 100644
index 00000000000..e39e0797013
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/AddNewApp.js
@@ -0,0 +1,336 @@
+import React from "react";
+import "antd/dist/antd.css";
+import {
+ PageHeader,
+ Typography,
+ Card,
+ Steps,
+ Button,
+ message,
+ Row,
+ Col,
+ Tag,
+ Tooltip,
+ Input,
+ Icon,
+ Select,
+ Switch,
+ Form,
+ Upload,
+ Divider
+} from "antd";
+import Step1 from "./Step1";
+import Step2 from "./Step2";
+import Step3 from "./Step3";
+import styles from "./Style.less";
+import IconImage from "./IconImg";
+import UploadScreenshots from "./UploadScreenshots";
+
+const Paragraph = Typography;
+const Dragger = Upload.Dragger;
+const routes = [
+ {
+ path: 'index',
+ breadcrumbName: 'store',
+ },
+ {
+ path: 'first',
+ breadcrumbName: 'dashboard',
+ },
+ {
+ path: 'second',
+ breadcrumbName: 'add new app',
+ },
+];
+
+const props = {
+ name: 'file',
+ multiple: false,
+ action: '//jsonplaceholder.typicode.com/posts/',
+ onChange(info) {
+ const status = info.file.status;
+ if (status !== 'uploading') {
+ console.log(info.file, info.fileList);
+ }
+ if (status === 'done') {
+ message.success(`${info.file.name} file uploaded successfully.`);
+ } else if (status === 'error') {
+ message.error(`${info.file.name} file upload failed.`);
+ }
+ },
+};
+
+const Step = Steps.Step;
+
+const steps = [{
+ title: 'First',
+ content: Step1
+}, {
+ title: 'Second',
+ content: Step2,
+}, {
+ title: 'Last',
+ content: Step3,
+}];
+
+
+const {Option} = Select;
+const {TextArea} = Input;
+const InputGroup = Input.Group;
+
+const formItemLayout = {
+ labelCol: {
+ span: 4,
+ },
+ wrapperCol: {
+ span: 20,
+ },
+};
+
+
+
+class EditableTagGroup extends React.Component {
+ state = {
+ tags: [],
+ inputVisible: false,
+ inputValue: '',
+ };
+
+ handleClose = (removedTag) => {
+ const tags = this.state.tags.filter(tag => tag !== removedTag);
+ console.log(tags);
+ this.setState({tags});
+ }
+
+ showInput = () => {
+ this.setState({inputVisible: true}, () => this.input.focus());
+ }
+
+ handleInputChange = (e) => {
+ this.setState({inputValue: e.target.value});
+ }
+
+ handleInputConfirm = () => {
+ const {inputValue} = this.state;
+ let {tags} = this.state;
+ if (inputValue && tags.indexOf(inputValue) === -1) {
+ tags = [...tags, inputValue];
+ }
+ console.log(tags);
+ this.setState({
+ tags,
+ inputVisible: false,
+ inputValue: '',
+ });
+ }
+
+ saveInputRef = input => this.input = input
+
+ render() {
+ const {tags, inputVisible, inputValue} = this.state;
+ return (
+
+ {tags.map((tag, index) => {
+ const isLongTag = tag.length > 20;
+ const tagElem = (
+ this.handleClose(tag)}>
+ {isLongTag ? `${tag.slice(0, 20)}...` : tag}
+
+ );
+ return isLongTag ? {tagElem} : tagElem;
+ })}
+ {inputVisible && (
+
+ )}
+ {!inputVisible && (
+
+ New Tag
+
+ )}
+
+ );
+ }
+}
+
+class AddNewApp extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ current: 0,
+ };
+ }
+
+ tags = [];
+
+ addTag(key, value){
+ this.tags.push({value} );
+ }
+
+ next() {
+ const current = this.state.current + 1;
+ this.setState({current});
+ }
+
+ prev() {
+ const current = this.state.current - 1;
+ this.setState({current});
+ }
+
+
+ render() {
+ const {current} = this.state;
+ const Content = steps[current].content;
+ this.addTag('1','Lorem');
+ this.addTag('2','Ipsum');
+ return (
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempo.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Application
+
+
+
+
+
+ Click or drag file to this area to
+ upload
+ Support for a single or bulk upload.
+ Strictly prohibit from uploading company data or other band
+ files
+
+
+
+
+ Icon
+
+
+
+ Banner
+
+
+
+
+
+
+
+ Screenshots
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default AddNewApp;
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/IconImg.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/IconImg.js
new file mode 100644
index 00000000000..b56dedb034d
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/IconImg.js
@@ -0,0 +1,66 @@
+import React from "react";
+import { Upload, Icon, message } from 'antd';
+
+function getBase64(img, callback) {
+ const reader = new FileReader();
+ reader.addEventListener('load', () => callback(reader.result));
+ reader.readAsDataURL(img);
+}
+
+function beforeUpload(file) {
+ const isJPG = file.type === 'image/jpeg';
+ if (!isJPG) {
+ message.error('You can only upload JPG file!');
+ }
+ const isLt2M = file.size / 1024 / 1024 < 2;
+ if (!isLt2M) {
+ message.error('Image must smaller than 2MB!');
+ }
+ return isJPG && isLt2M;
+}
+
+
+class IconImage extends React.Component {
+ state = {
+ loading: false,
+ };
+
+ handleChange = (info) => {
+ if (info.file.status === 'uploading') {
+ this.setState({ loading: true });
+ return;
+ }
+ if (info.file.status === 'done') {
+ // Get this url from response in real world.
+ getBase64(info.file.originFileObj, imageUrl => this.setState({
+ imageUrl,
+ loading: false,
+ }));
+ }
+ }
+
+ render() {
+ const uploadButton = (
+
+ );
+ const imageUrl = this.state.imageUrl;
+ return (
+
+ {imageUrl ? : uploadButton}
+
+ );
+ }
+}
+
+export default IconImage;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step1.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step1.js
new file mode 100644
index 00000000000..24d71944311
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step1.js
@@ -0,0 +1,153 @@
+import React from "react";
+import {Form, Input, Button, Select, Divider, Tag, Tooltip, Icon, Checkbox, Row, Col} from "antd";
+import styles from './Style.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const InputGroup = Input.Group;
+
+const formItemLayout = {
+ labelCol: {
+ span: 8,
+ },
+ wrapperCol: {
+ span: 16,
+ },
+};
+
+class EditableTagGroup extends React.Component {
+ state = {
+ tags: [],
+ inputVisible: false,
+ inputValue: '',
+ };
+
+ handleClose = (removedTag) => {
+ const tags = this.state.tags.filter(tag => tag !== removedTag);
+ console.log(tags);
+ this.setState({ tags });
+ }
+
+ showInput = () => {
+ this.setState({ inputVisible: true }, () => this.input.focus());
+ }
+
+ handleInputChange = (e) => {
+ this.setState({ inputValue: e.target.value });
+ }
+
+ handleInputConfirm = () => {
+ const { inputValue } = this.state;
+ let { tags } = this.state;
+ if (inputValue && tags.indexOf(inputValue) === -1) {
+ tags = [...tags, inputValue];
+ }
+ console.log(tags);
+ this.setState({
+ tags,
+ inputVisible: false,
+ inputValue: '',
+ });
+ }
+
+ saveInputRef = input => this.input = input
+
+ render() {
+ const { tags, inputVisible, inputValue } = this.state;
+ return (
+
+ {tags.map((tag, index) => {
+ const isLongTag = tag.length > 20;
+ const tagElem = (
+ this.handleClose(tag)}>
+ {isLongTag ? `${tag.slice(0, 20)}...` : tag}
+
+ );
+ return isLongTag ? {tagElem} : tagElem;
+ })}
+ {inputVisible && (
+
+ )}
+ {!inputVisible && (
+
+ New Tag
+
+ )}
+
+ );
+ }
+}
+
+class Step1 extends React.Component {
+ render() {
+ console.log("hhhoohh");
+ return (
+
+
+
+ Android
+ iOS
+
+
+
+
+ Enterprise
+
+
+
+
+
+
+
+
+
+
+ Travel
+ Entertainment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default Step1;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step2.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step2.js
new file mode 100644
index 00000000000..103da1a9c2b
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step2.js
@@ -0,0 +1,12 @@
+import React from "react"
+
+class Step2 extends React.Component {
+ render() {
+ console.log("hhhoohh");
+ return (
+ tttoooeeee
+ );
+ }
+}
+
+export default Step2;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step3.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step3.js
new file mode 100644
index 00000000000..6ddba9f8e21
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step3.js
@@ -0,0 +1,12 @@
+import React from "react"
+
+class Step3 extends React.Component {
+ render() {
+ console.log("hhhoohh");
+ return (
+ tttoooeeee
+ );
+ }
+}
+
+export default Step3;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Style.less b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Style.less
new file mode 100644
index 00000000000..6deac5739b1
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Style.less
@@ -0,0 +1,4 @@
+.stepForm {
+ max-width: 500px;
+ margin: 40px auto 0;
+}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/UploadScreenshots.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/UploadScreenshots.js
new file mode 100644
index 00000000000..057e0f75be6
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/UploadScreenshots.js
@@ -0,0 +1,49 @@
+import React from "react";
+import { Upload, Icon, Modal} from 'antd';
+
+
+class UploadScreenshots extends React.Component {
+ state = {
+ previewVisible: false,
+ previewImage: '',
+ fileList: [],
+ };
+
+ handleCancel = () => this.setState({ previewVisible: false });
+
+ handlePreview = (file) => {
+ this.setState({
+ previewImage: file.url || file.thumbUrl,
+ previewVisible: true,
+ });
+ };
+
+ handleChange = ({ fileList }) => this.setState({ fileList });
+
+ render() {
+ const { previewVisible, previewImage, fileList } = this.state;
+ const uploadButton = (
+
+ );
+ return (
+
+
+ {fileList.length >= 3 ? null : uploadButton}
+
+
+
+
+
+ );
+ }
+}
+export default UploadScreenshots;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/components/AddTagModal.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/components/AddTagModal.js
new file mode 100644
index 00000000000..c0644ec1492
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/components/AddTagModal.js
@@ -0,0 +1,49 @@
+import React from "react";
+import { Upload, Icon, Modal} from 'antd';
+
+
+class AddTagModal extends React.Component {
+ state = {
+ previewVisible: false,
+ previewImage: '',
+ fileList: [],
+ };
+
+ handleCancel = () => this.setState({ previewVisible: false });
+
+ handlePreview = (file) => {
+ this.setState({
+ previewImage: file.url || file.thumbUrl,
+ previewVisible: true,
+ });
+ };
+
+ handleChange = ({ fileList }) => this.setState({ fileList });
+
+ render() {
+ const { previewVisible, previewImage, fileList } = this.state;
+ const uploadButton = (
+
+ );
+ return (
+
+
+ {fileList.length >= 3 ? null : uploadButton}
+
+
+
+
+
+ );
+ }
+}
+export default AddTagModal;
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/Apps.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/Apps.js
new file mode 100644
index 00000000000..90e68ed1d03
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/Apps.js
@@ -0,0 +1,60 @@
+import React from "react";
+import "antd/dist/antd.css";
+import {PageHeader, Typography,Input, Button, Row, Col} from "antd";
+import AppList from "../../../components/apps/AppList";
+import ReleaseModal from "../../../components/apps/ReleaseModal";
+
+const Search = Input.Search;
+
+const routes = [
+ {
+ path: 'index',
+ breadcrumbName: 'store',
+ },
+ {
+ path: 'first',
+ breadcrumbName: 'Dashboard',
+ },
+ {
+ path: 'second',
+ breadcrumbName: 'Apps',
+ },
+];
+
+
+class Apps extends React.Component {
+ routes;
+ constructor(props) {
+ super(props);
+ this.routes = props.routes;
+
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ console.log(value)}
+ style={{ width: 200}}
+ />
+ Advanced Search
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default Apps;
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js
new file mode 100644
index 00000000000..27afea477e3
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js
@@ -0,0 +1,104 @@
+import React from "react";
+import '../../../../App.css';
+import {Skeleton, Typography, Row, Col, Card} from "antd";
+import {connect} from "react-redux";
+import ReleaseView from "../../../../components/apps/release/ReleaseView";
+import {getRelease, setLoading} from "../../../../js/actions";
+
+const {Title} = Typography;
+
+const routes = [
+ {
+ path: 'index',
+ breadcrumbName: 'store',
+ },
+ {
+ path: 'first',
+ breadcrumbName: 'Dashboard',
+ },
+ {
+ path: 'second',
+ breadcrumbName: 'Apps',
+ },
+];
+
+const mapStateToProps = state => {
+ return {
+ release: state.release,
+ releaseLoading: state.loadingState.release
+ }
+};
+
+const mapDispatchToProps = dispatch => ({
+ getRelease: (uuid) => dispatch(getRelease(uuid)),
+ setLoading: (stateToLoad) => dispatch(setLoading(stateToLoad))
+});
+
+class ConnectedRelease extends React.Component {
+ routes;
+
+ constructor(props) {
+ super(props);
+ this.routes = props.routes;
+
+ }
+
+ componentDidMount() {
+ const {uuid} = this.props.match.params;
+ this.props.setLoading("release");
+ this.props.getRelease(uuid);
+ }
+
+ render() {
+
+ const release = this.props.release;
+ let content = No Releases Found ;
+
+ if (release != null) {
+ content = ;
+ }
+
+
+ return (
+
+
+
+
+
+
+
+
+ {content}
+
+
+
+
+
+
+ );
+
+
+ // //todo remove uppercase
+ // return (
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // );
+ }
+}
+
+const Release = connect(mapStateToProps, mapDispatchToProps)(ConnectedRelease);
+
+export default Release;
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/webpack.config.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/webpack.config.js
index dcb16b0f5e3..19c52f4fbed 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/webpack.config.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/webpack.config.js
@@ -21,6 +21,9 @@ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const config = {
devtool: "source-map",
+ output: {
+ publicPath: '/store/' // <---- this
+ },
watch: false,
resolve: {
alias: {