diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json index 2abcaecd713..469943d5b72 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json @@ -13,6 +13,7 @@ "acorn": "^6.1.1", "antd": "^3.15.0", "axios": "^0.18.0", + "dagre": "^0.8.4", "keymirror": "^0.1.1", "react": "^16.8.4", "react-dom": "^16.8.4", @@ -21,7 +22,8 @@ "react-router-config": "^5.0.0", "react-router-dom": "latest", "react-scripts": "2.1.8", - "redux-thunk": "^2.3.0" + "redux-thunk": "^2.3.0", + "storm-react-diagrams": "^5.2.1" }, "devDependencies": { "@babel/core": "^7.0.0", diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycle.css b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycle.css new file mode 100644 index 00000000000..912a757ebae --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycle.css @@ -0,0 +1,7 @@ +.srd-diagram{ + height: 100%; + min-height: 300px; + background-color: #3c3c3c !important; + background-image: linear-gradient(0deg, transparent 24%, rgba(255, 255, 255, 0.05) 25%, rgba(255, 255, 255, 0.05) 26%, transparent 27%, transparent 74%, rgba(255, 255, 255, 0.05) 75%, rgba(255, 255, 255, 0.05) 76%, transparent 77%, transparent), linear-gradient(90deg, transparent 24%, rgba(255, 255, 255, 0.05) 25%, rgba(255, 255, 255, 0.05) 26%, transparent 27%, transparent 74%, rgba(255, 255, 255, 0.05) 75%, rgba(255, 255, 255, 0.05) 76%, transparent 77%, transparent); + background-size: 50px 50px; +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycle.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycle.js new file mode 100644 index 00000000000..1d76067d7f1 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycle.js @@ -0,0 +1,37 @@ +import React from "react"; +import LifeCycleGraph from "./LifeCycleGraph"; +import {connect} from "react-redux"; +import {getLifecycle, openReleasesModal} from "../../../js/actions"; + +const mapDispatchToProps = dispatch => ({ + getLifecycle: () => dispatch(getLifecycle()) +}); + +const mapStateToProps = state => { + return { + lifecycle: state.lifecycle + }; +}; + +class ConnectedLifeCycle extends React.Component { + componentDidMount() { + this.props.getLifecycle(); + } + + render() { + console.log(); + const lifecycle = this.props.lifecycle; + if(lifecycle != null){ + return ( + + ); + + }else { + return null; + } + } +} + +const LifeCycle = connect(mapStateToProps, mapDispatchToProps)(ConnectedLifeCycle); + +export default LifeCycle; \ No newline at end of file diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycleGraph.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycleGraph.js new file mode 100644 index 00000000000..f5051decd19 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/LifeCycleGraph.js @@ -0,0 +1,90 @@ +import React from "react"; +import * as SRD from "storm-react-diagrams"; +import "storm-react-diagrams/dist/style.min.css"; +import "./LifeCycle.css"; +import {distributeElements} from "../../../js/utils/dagre-utils.ts"; + +const inPortName = "IN"; +const outPortName = "OUT"; + +class LifeCycleGraph extends React.Component { + render() { + + const lifecycle = this.props.lifecycle; + const nodes = []; + const links = []; + + const engine = new SRD.DiagramEngine(); + engine.installDefaultFactories(); + + const model = new SRD.DiagramModel(); + const nextStates = lifecycle[this.props.currentStatus].proceedingStates; + + + Object.keys(lifecycle).forEach((stateName) => { + let color = "rgb(83, 92, 104)"; + if (stateName === this.props.currentStatus) { + color = "rgb(192,255,0)"; + }else if(nextStates.includes(stateName)){ + color = "rgb(0,192,255)"; + } + const node = createNode(stateName, color); + // node.addPort() + nodes.push(node); + lifecycle[stateName].node = node; + }); + + Object.keys(lifecycle).forEach((stateName) => { + const state = lifecycle[stateName]; + //todo: remove checking property + if (state.hasOwnProperty("proceedingStates")) { + + state.proceedingStates.forEach((proceedingState) => { + links.push(connectNodes(state.node, lifecycle[proceedingState].node)); + }); + } + }); + + nodes.forEach((node) => { + model.addNode(node); + }); + links.forEach((link) => { + model.addLink(link); + }); + + + let distributedModel = getDistributedModel(engine, model); + engine.setDiagramModel(distributedModel); + + return ( +
+ +
+ ); + } +} + +function getDistributedModel(engine, model) { + const serialized = model.serializeDiagram(); + const distributedSerializedDiagram = distributeElements(serialized); + + //deserialize the model + let deSerializedModel = new SRD.DiagramModel(); + deSerializedModel.deSerializeDiagram(distributedSerializedDiagram, engine); + return deSerializedModel; +} + +function createNode(name, color) { + const node = new SRD.DefaultNodeModel(name, color); + node.addPort(new SRD.DefaultPortModel(true, inPortName, " ")); + node.addPort(new SRD.DefaultPortModel(false, outPortName, " ")); + return node; +} + +let count = 0; + +function connectNodes(nodeFrom, nodeTo) { + return nodeFrom.getPort(outPortName).link(nodeTo.getPort(inPortName)); +} + +export default LifeCycleGraph; \ No newline at end of file diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/actions/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/actions/index.js index de94675a262..f89df5fbf09 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/actions/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/actions/index.js @@ -14,7 +14,6 @@ export const getApps = () => dispatch => { if (res.data.data.hasOwnProperty("applications")) { apps = res.data.data.applications; } - console.log(res.data); dispatch({type: ActionTypes.GET_APPS, payload: apps}); } @@ -35,8 +34,6 @@ export const getRelease = (uuid) => dispatch => { ).then(res => { if (res.status === 200) { let release = res.data.data; - - console.log(res.data); dispatch({type: ActionTypes.GET_RELEASE, payload: release}); } @@ -50,7 +47,6 @@ export const getRelease = (uuid) => dispatch => { }; export const openReleasesModal = (app) => dispatch => { - console.log(app); dispatch({ type: ActionTypes.OPEN_RELEASES_MODAL, payload: { @@ -59,3 +55,19 @@ export const openReleasesModal = (app) => dispatch => { }); }; +export const getLifecycle = ()=> dispatch =>{ + const request = "method=get&content-type=application/json&payload={}&api-endpoint=/application-mgt-publisher/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/publisher/login'; + } + }); +}; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/constants/ActionTypes.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/constants/ActionTypes.js index 211bd739a4f..dd66861ec7a 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/constants/ActionTypes.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/constants/ActionTypes.js @@ -1,16 +1,12 @@ import keyMirror from 'keymirror'; -// export const LOGIN = "LOGIN"; -// export const GET_APPS = "GET_APPS"; -// export const OPEN_RELEASES_MODAL = "OPEN_RELEASES_MODAL"; -// export const CLOSE_RELEASES_MODAL = "CLOSE_RELEASES_MODAL"; - const ActionTypes = keyMirror({ LOGIN: null, GET_APPS: null, OPEN_RELEASES_MODAL: null, CLOSE_RELEASES_MODAL: null, - GET_RELEASE: null + GET_RELEASE: null, + GET_LIFECYCLE: null }); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/reducers/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/reducers/index.js index 5a9b3796574..0d720a35786 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/reducers/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/reducers/index.js @@ -6,7 +6,8 @@ const initialState = { visible: false, app: null }, - release: null + release: null, + lifecycle: null }; function rootReducer(state = initialState, action) { @@ -21,11 +22,14 @@ function rootReducer(state = initialState, action) { app: action.payload.app } }); - }else if(action.type === ActionTypes.GET_RELEASE){ + } else if (action.type === ActionTypes.GET_RELEASE) { return Object.assign({}, state, { release: action.payload }); - + } else if (action.type === ActionTypes.GET_LIFECYCLE) { + return Object.assign({}, state, { + lifecycle: action.payload + }); } return state; } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/utils/dagre-utils.ts b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.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.publisher.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.publisher.ui/react-app/src/pages/dashboard/Dashboard.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js index 396e335f2e2..ea9d762d9b8 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js @@ -38,7 +38,7 @@ class Dashboard extends React.Component { - + {this.state.routes.map((route) => ( ))} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js index aad15ec595c..c01c0b9283f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js @@ -3,6 +3,7 @@ import {PageHeader, Typography, Input, Button, Row, Col, Avatar, Card} from "ant import {connect} from "react-redux"; import ReleaseView from "../../../../components/apps/release/ReleaseView"; import {getRelease} from "../../../../js/actions"; +import LifeCycle from "../../../../components/apps/release/LifeCycle"; const Search = Input.Search; const {Title} = Typography; @@ -54,6 +55,8 @@ class ConnectedRelease extends React.Component { ); } + + //todo remove uppercase return (
- + + + + + +