forked from community/device-mgt-core
Add Life-cycle Graph for APPM Publisher See merge request entgra/carbon-device-mgt!96feature/appm-store/pbac
commit
1c5bb8c088
@ -1,90 +1,117 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import * as SRD from "storm-react-diagrams";
|
import {Graph} from 'react-d3-graph';
|
||||||
import "storm-react-diagrams/dist/style.min.css";
|
import {connect} from "react-redux";
|
||||||
import "./LifeCycle.css";
|
import {getLifecycle, openLifecycleModal} from "../../../js/actions";
|
||||||
import {distributeElements} from "../../../js/utils/dagre-utils.ts";
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
openLifecycleModal: (nextState) => dispatch(openLifecycleModal(nextState))
|
||||||
|
});
|
||||||
|
|
||||||
|
// the graph configuration, you only need to pass down properties
|
||||||
|
// that you want to override, otherwise default ones will be used
|
||||||
|
const myConfig = {
|
||||||
|
nodeHighlightBehavior: true,
|
||||||
|
directed: true,
|
||||||
|
height: 400,
|
||||||
|
d3: {
|
||||||
|
alphaTarget: 0.05,
|
||||||
|
gravity: -200,
|
||||||
|
linkLength: 200,
|
||||||
|
linkStrength: 1
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
color: "#d3d3d3",
|
||||||
|
fontColor: "black",
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: "normal",
|
||||||
|
highlightFontSize: 12,
|
||||||
|
highlightFontWeight: "bold",
|
||||||
|
highlightStrokeColor: "SAME",
|
||||||
|
highlightStrokeWidth: 1.5,
|
||||||
|
labelProperty: "id",
|
||||||
|
mouseCursor: "pointer",
|
||||||
|
opacity: 1,
|
||||||
|
strokeColor: "none",
|
||||||
|
strokeWidth: 1.5,
|
||||||
|
svg: "",
|
||||||
|
symbolType: "circle",
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
highlightColor: 'lightblue'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectedLifeCycleGraph extends React.Component {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.nextStates = null;
|
||||||
|
this.onClickNode = this.onClickNode.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
const inPortName = "IN";
|
onClickNode = function(nodeId) {
|
||||||
const outPortName = "OUT";
|
const nextStates = this.nextStates;
|
||||||
|
if(nextStates.includes(nodeId)){
|
||||||
|
this.props.openLifecycleModal(nodeId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class LifeCycleGraph extends React.Component {
|
|
||||||
render() {
|
render() {
|
||||||
|
// graph payload (with minimalist structure)
|
||||||
|
|
||||||
const lifecycle = this.props.lifecycle;
|
const lifecycle = this.props.lifecycle;
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
const links = [];
|
const links = [];
|
||||||
|
this.nextStates = lifecycle[this.props.currentStatus].proceedingStates;
|
||||||
const engine = new SRD.DiagramEngine();
|
|
||||||
engine.installDefaultFactories();
|
|
||||||
|
|
||||||
const model = new SRD.DiagramModel();
|
|
||||||
const nextStates = lifecycle[this.props.currentStatus].proceedingStates;
|
|
||||||
|
|
||||||
|
|
||||||
Object.keys(lifecycle).forEach((stateName) => {
|
Object.keys(lifecycle).forEach((stateName) => {
|
||||||
|
const state = lifecycle[stateName];
|
||||||
let color = "rgb(83, 92, 104)";
|
let color = "rgb(83, 92, 104)";
|
||||||
if (stateName === this.props.currentStatus) {
|
if (stateName === this.props.currentStatus) {
|
||||||
color = "rgb(192,255,0)";
|
color = "rgb(39, 174, 96)";
|
||||||
}else if(nextStates.includes(stateName)){
|
} else if (this.nextStates.includes(stateName)) {
|
||||||
color = "rgb(0,192,255)";
|
color = "rgb(0,192,255)";
|
||||||
}
|
}
|
||||||
const node = createNode(stateName, color);
|
let node = {
|
||||||
// node.addPort()
|
id: stateName,
|
||||||
|
color: color
|
||||||
|
};
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
lifecycle[stateName].node = node;
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(lifecycle).forEach((stateName) => {
|
|
||||||
const state = lifecycle[stateName];
|
|
||||||
//todo: remove checking property
|
//todo: remove checking property
|
||||||
if (state.hasOwnProperty("proceedingStates")) {
|
if (state.hasOwnProperty("proceedingStates")) {
|
||||||
|
|
||||||
state.proceedingStates.forEach((proceedingState) => {
|
state.proceedingStates.forEach((proceedingState) => {
|
||||||
links.push(connectNodes(state.node, lifecycle[proceedingState].node));
|
let link = {
|
||||||
|
source: stateName,
|
||||||
|
target: proceedingState
|
||||||
|
};
|
||||||
|
links.push(link);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
const data = {
|
||||||
model.addNode(node);
|
nodes: nodes,
|
||||||
});
|
links: links
|
||||||
links.forEach((link) => {
|
};
|
||||||
model.addLink(link);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let distributedModel = getDistributedModel(engine, model);
|
|
||||||
engine.setDiagramModel(distributedModel);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{height: 900}}>
|
<div>
|
||||||
<SRD.DiagramWidget diagramEngine={engine} maxNumberPointsPerLink={10} smartRouting={true}/>
|
<Graph
|
||||||
|
id="graph-id" // id is mandatory, if no id is defined rd3g will throw an error
|
||||||
|
data={data}
|
||||||
|
config={myConfig}
|
||||||
|
onClickNode={this.onClickNode}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDistributedModel(engine, model) {
|
const LifeCycleGraph = connect(null,mapDispatchToProps)(ConnectedLifeCycleGraph);
|
||||||
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;
|
export default LifeCycleGraph;
|
@ -0,0 +1,112 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {Modal, Typography, Icon, Input, Form, Checkbox, Button} from 'antd';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {closeLifecycleModal, updateLifecycleState} from "../../../js/actions";
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
// connecting state.releaseView with the component
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
nextState: state.lifecycleModal.nextState,
|
||||||
|
visible: state.lifecycleModal.visible
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
closeLifecycleModal : () => dispatch(closeLifecycleModal()),
|
||||||
|
updateLifecycleState : (uuid, nextState, reason) => dispatch(updateLifecycleState(uuid, nextState, reason))
|
||||||
|
});
|
||||||
|
|
||||||
|
const Text = Typography;
|
||||||
|
|
||||||
|
class ConnectedLifecycleModal extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
loading: false,
|
||||||
|
visible: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps !== this.props) {
|
||||||
|
this.setState({
|
||||||
|
visible: nextProps.visible
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showModal = () => {
|
||||||
|
this.setState({
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleOk = (e) => {
|
||||||
|
this.setState({
|
||||||
|
visible: false,
|
||||||
|
});
|
||||||
|
this.props.closeLifecycleModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCancel = (e) => {
|
||||||
|
this.setState({
|
||||||
|
visible: false,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
this.props.closeLifecycleModal();
|
||||||
|
};
|
||||||
|
handleSubmit = event => {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
event.preventDefault();
|
||||||
|
console.log(this.reason);
|
||||||
|
console.log("uuid", this.props.uuid);
|
||||||
|
this.props.updateLifecycleState(this.props.uuid, this.props.nextState, this.reason.state.value)
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.nextState != null) {
|
||||||
|
const nextState = this.props.nextState;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Modal
|
||||||
|
title="Change State"
|
||||||
|
visible={this.state.visible}
|
||||||
|
onCancel={this.handleCancel}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<Title level={4}>{this.props.currentStatus} <Icon type="arrow-right" /> {nextState}</Title>
|
||||||
|
<form onSubmit={this.handleSubmit}>
|
||||||
|
<Form.Item>
|
||||||
|
<label htmlFor="username">Reason</label>
|
||||||
|
|
||||||
|
<Input placeholder="Enter the reason" ref={(input) => this.reason = input}/>
|
||||||
|
</Form.Item>
|
||||||
|
{/*<Form.Item>*/}
|
||||||
|
{/*<TextArea*/}
|
||||||
|
{/*placeholder="Please enter the reason..."*/}
|
||||||
|
{/*ref={(input) => this.input = input}*/}
|
||||||
|
{/*autosize*/}
|
||||||
|
{/*/>*/}
|
||||||
|
{/*</Form.Item>*/}
|
||||||
|
<Button key="back" onClick={this.handleCancel}>
|
||||||
|
Cancel
|
||||||
|
</Button>,
|
||||||
|
<Button key="submit" type="primary" htmlType="submit" loading={this.state.loading}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LifecycleModal = connect(mapStateToProps, mapDispatchToProps)(ConnectedLifecycleModal);
|
||||||
|
|
||||||
|
export default LifecycleModal;
|
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
/* --- Shape for the nodes --- */
|
||||||
|
|
||||||
|
.node {
|
||||||
|
width: 75px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 20% 20% 20% 20%;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.node .name {
|
||||||
|
padding: 5%;
|
||||||
|
font-size: 0.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: white;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import "./CustomNode.css";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders a person's name and gender, along with icons
|
||||||
|
* representing if they have a driver license for bike and / or car.
|
||||||
|
* @param {Object} props component props to render.
|
||||||
|
*/
|
||||||
|
function CustomNode({ node }) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="node" style={{backgroundColor: node.color}}>
|
||||||
|
<div className="name">{node.id}</div>
|
||||||
|
|
||||||
|
{/*<div className="flex-container fill-space flex-container-row">*/}
|
||||||
|
{/*<div className="fill-space">*/}
|
||||||
|
{/*<div*/}
|
||||||
|
{/*className="icon"*/}
|
||||||
|
{/*style={{ backgroundImage: `url('${isMale ? ICON_TYPES.MAN : ICON_TYPES.WOMAN}')` }}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
|
||||||
|
{/*<div className="icon-bar">*/}
|
||||||
|
{/*{person.hasBike && (*/}
|
||||||
|
{/*<div className="icon" style={{ backgroundImage: `url('${ICON_TYPES.BIKE}')` }} />*/}
|
||||||
|
{/*)}*/}
|
||||||
|
{/*{person.hasCar && <div className="icon" style={{ backgroundImage: `url('${ICON_TYPES.CAR}')` }} />}*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomNode;
|
@ -0,0 +1,98 @@
|
|||||||
|
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);
|
||||||
|
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);
|
||||||
|
// node.addListener({
|
||||||
|
// selectionChanged: (node, isSelected) => {
|
||||||
|
// console.log(isSelected);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
links.forEach((link) => {
|
||||||
|
model.addLink(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let distributedModel = getDistributedModel(engine, model);
|
||||||
|
engine.setDiagramModel(distributedModel);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{height: 500}}>
|
||||||
|
<SRD.DiagramWidget diagramEngine={engine} maxNumberPointsPerLink={10} smartRouting={true}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
console.log(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LifeCycleGraph;
|
Loading…
Reference in new issue