From 2ca6637bb6ef5f7a00706fb30296eaa6c80369bd Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Tue, 28 May 2019 08:20:41 +0000 Subject: [PATCH] Create APPM's basic store view --- .../react-app/src/App.css | 2 +- .../components/apps/release/ReleaseView.js | 83 +++-- .../react-app/.env | 0 .../react-app/package.json | 18 +- .../react-app/public/conf/config.json | 12 +- .../react-app/public/conf/manifest.json | 4 +- .../public/themes/default/default-theme.css | 4 +- .../react-app/server/index.js | 17 - .../react-app/src/App.css | 60 ++-- .../react-app/src/App.js | 35 +- .../src/components/RouteWithSubRoutes.js | 19 + .../react-app/src/components/apps/AppCard.js | 71 ++++ .../react-app/src/components/apps/AppList.js | 38 ++ .../src/components/apps/ReleaseModal.js | 85 +++++ .../apps/release/DetailedRating.css | 72 ++++ .../components/apps/release/DetailedRating.js | 91 +++++ .../components/apps/release/ReleaseView.js | 61 ++++ .../apps/release/images/ImgViewer.js | 45 +++ .../react-app/src/index.css | 24 ++ .../react-app/src/index.html | 9 +- .../react-app/src/index.js | 54 +-- .../react-app/src/js/actions/index.js | 168 +++++++++ .../react-app/src/js/constants/ActionTypes.js | 17 + .../react-app/src/js/reducers/index.js | 83 +++++ .../react-app/src/js/store/index.js | 5 + .../react-app/src/js/utils/dagre-utils.ts | 56 +++ .../react-app/src/pages/Login.js | 68 +++- .../src/pages/dashboard/Dashboard.js | 82 ++--- .../src/pages/dashboard/Dashboard.less | 12 +- .../pages/dashboard/add-new-app/AddNewApp.js | 336 ++++++++++++++++++ .../pages/dashboard/add-new-app/IconImg.js | 66 ++++ .../src/pages/dashboard/add-new-app/Step1.js | 153 ++++++++ .../src/pages/dashboard/add-new-app/Step2.js | 12 + .../src/pages/dashboard/add-new-app/Step3.js | 12 + .../pages/dashboard/add-new-app/Style.less | 4 + .../add-new-app/UploadScreenshots.js | 49 +++ .../add-new-app/components/AddTagModal.js | 49 +++ .../src/pages/dashboard/apps/Apps.js | 60 ++++ .../pages/dashboard/apps/release/Release.js | 104 ++++++ .../react-app/webpack.config.js | 3 + 40 files changed, 1975 insertions(+), 168 deletions(-) delete mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/.env delete mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/server/index.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/RouteWithSubRoutes.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/ReleaseModal.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.css create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/DetailedRating.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/images/ImgViewer.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/actions/index.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/constants/ActionTypes.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/reducers/index.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/store/index.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/utils/dagre-utils.ts create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/AddNewApp.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/IconImg.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step1.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step2.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Step3.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/Style.less create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/UploadScreenshots.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/add-new-app/components/AddTagModal.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/Apps.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js 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 adbde74ba5..048a7eb7ab 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 412589062b..effa0d7ec6 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 ( -
- - - icon - - - App Name - {release.version}
- {release.description}
- - -
- - - - -
- - -
-
- - {release.screenshots.map((screenshotUrl)=>{ - return ( - - - - ) - })} - +
+
+ + + icon + + + App Name + {release.version}
+ {release.description}
+ + +
+ + + + +
+ +
+ + + {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 e69de29bb2..0000000000 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 af55bd6b6b..5075b4fe03 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 63678cb541..c2e66dda14 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 2a700bb50e..60f712111e 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 32cb3c22b9..d6e82a6745 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 1f8cf6c4d7..0000000000 --- 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 b41d297cab..1e8064d0d9 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 5cf9be115e..c730379bdb 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 0000000000..41cb84caf8 --- /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 0000000000..c77feb13a5 --- /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 = ( +
+ + + + icon + {/**/} + + + {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 0000000000..e41c30290a --- /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 0000000000..2265aa6d3f --- /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 0000000000..9e7f3bc579 --- /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 0000000000..6430d740e2 --- /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 0000000000..3a87ac38b8 --- /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 ( +
+
+ + + icon + + + App Name + Version : {release.version}

+ + + +
+ + + + +
+ +
+ + + + + + + {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 0000000000..447531aa9a --- /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 8b45ed5931..fa1cc5934e 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 98499e9ecf..d469799e30 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 96931fc788..bf001fa205 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 0000000000..4fdcd14c1b --- /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 0000000000..bd47f22074 --- /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 0000000000..fcf1aa7db6 --- /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 0000000000..04957eb39a --- /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 0000000000..58a006f9a9 --- /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 f4faa2a348..38cb11a1c5 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 - 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 6088af4b6f..da92c72a2d 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
-
-
- ©2019 entgra.io -
-
+ +
+ ©2019 entgra.io +
+ +
); } } 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 483f13e117..dc7904b51b 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 0000000000..e39e079701 --- /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(); + } + + 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. + +
+
+
+
+ + + + + +
+
+ + + + + + + + + + +