mirror of
https://github.com/openziti/zrok.git
synced 2025-06-03 08:35:53 +02:00
detail view scaffolding; selection management (#107)
This commit is contained in:
parent
dcf662ebff
commit
8d81eee3b0
@ -4,6 +4,7 @@ import Visualizer from "./visualizer/Visualizer";
|
|||||||
import Enable from "./modals/Enable";
|
import Enable from "./modals/Enable";
|
||||||
import Version from "./modals/Version";
|
import Version from "./modals/Version";
|
||||||
import * as metadata from "../api/metadata";
|
import * as metadata from "../api/metadata";
|
||||||
|
import Detail from "./Detail";
|
||||||
|
|
||||||
const Console = (props) => {
|
const Console = (props) => {
|
||||||
const [showEnableModal, setShowEnableModal] = useState(false);
|
const [showEnableModal, setShowEnableModal] = useState(false);
|
||||||
@ -38,7 +39,10 @@ const Console = (props) => {
|
|||||||
mounted = false;
|
mounted = false;
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
}
|
}
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
const defaultSelection = {id: props.user.token, type: "account"};
|
||||||
|
const [selection, setSelection] = useState(defaultSelection);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container fluid={"xl"}>
|
<Container fluid={"xl"}>
|
||||||
@ -60,7 +64,14 @@ const Console = (props) => {
|
|||||||
</Navbar.Collapse>
|
</Navbar.Collapse>
|
||||||
</Container>
|
</Container>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<Visualizer user={props.user} overview={overview} />
|
<Visualizer
|
||||||
|
user={props.user}
|
||||||
|
overview={overview}
|
||||||
|
defaultSelection={defaultSelection}
|
||||||
|
selection={selection}
|
||||||
|
setSelection={setSelection}
|
||||||
|
/>
|
||||||
|
<Detail selection={selection} />
|
||||||
<Enable show={showEnableModal} onHide={closeEnableModal} token={props.user.token} />
|
<Enable show={showEnableModal} onHide={closeEnableModal} token={props.user.token} />
|
||||||
<Version show={showVersionModal} onHide={closeVersionModal} />
|
<Version show={showVersionModal} onHide={closeVersionModal} />
|
||||||
</Container>
|
</Container>
|
||||||
|
9
ui/src/console/Detail.js
Normal file
9
ui/src/console/Detail.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const Detail = (props) => {
|
||||||
|
return (
|
||||||
|
<div className={"detail-container"}>
|
||||||
|
<h1>{props.selection.id} ({props.selection.type})</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Detail;
|
@ -16,12 +16,11 @@ const Network = (props) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const paintNode = (node, ctx) => {
|
const paintNode = (node, ctx) => {
|
||||||
let nodeColor = "#555";
|
let nodeColor = "#636363";
|
||||||
let textColor = "white";
|
let textColor = "white";
|
||||||
switch(node.type) {
|
switch(node.type) {
|
||||||
case "environment":
|
case "environment":
|
||||||
nodeColor = "#777";
|
nodeColor = "#444";
|
||||||
textColor = "black";
|
|
||||||
break;
|
break;
|
||||||
case "service":
|
case "service":
|
||||||
nodeColor = "#291A66";
|
nodeColor = "#291A66";
|
||||||
@ -38,10 +37,15 @@ const Network = (props) => {
|
|||||||
roundRect(ctx, node.x - (nodeWidth / 2), node.y - 7, nodeWidth, 14, 1.25);
|
roundRect(ctx, node.x - (nodeWidth / 2), node.y - 7, nodeWidth, 14, 1.25);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
switch(node.type) {
|
if(node.selected) {
|
||||||
case "service":
|
ctx.strokeStyle = "#c4bdde";
|
||||||
ctx.strokeStyle = "#433482";
|
ctx.stroke();
|
||||||
ctx.stroke();
|
} else {
|
||||||
|
switch(node.type) {
|
||||||
|
case "service":
|
||||||
|
ctx.strokeStyle = "#433482";
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = textColor;
|
ctx.fillStyle = textColor;
|
||||||
@ -49,7 +53,7 @@ const Network = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nodeClicked = (node) => {
|
const nodeClicked = (node) => {
|
||||||
console.log("node clicked", node.label);
|
props.setSelection({id: node.id, type: node.type});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,13 +2,26 @@ import React, {useEffect, useState} from "react";
|
|||||||
import {Button} from "react-bootstrap";
|
import {Button} from "react-bootstrap";
|
||||||
import Network from "./Network";
|
import Network from "./Network";
|
||||||
import {mergeGraph} from "./graph";
|
import {mergeGraph} from "./graph";
|
||||||
|
import {isSelectionGone, markSelected} from "./selection";
|
||||||
|
|
||||||
const Visualizer = (props) => {
|
const Visualizer = (props) => {
|
||||||
const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []});
|
const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview));
|
setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview));
|
||||||
}, [props]);
|
|
||||||
|
if(isSelectionGone(networkGraph, props.selection)) {
|
||||||
|
// if the selection is no longer in the network graph...
|
||||||
|
console.log("resetting selection", props.selection);
|
||||||
|
props.setSelection(props.defaultSelection);
|
||||||
|
}
|
||||||
|
}, [props.overview]);
|
||||||
|
|
||||||
|
markSelected(networkGraph, props.selection);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
markSelected(networkGraph, props.selection);
|
||||||
|
}, [props.selection]);
|
||||||
|
|
||||||
// fgRef to access force graph controls from this component
|
// fgRef to access force graph controls from this component
|
||||||
let fgRef = () => { };
|
let fgRef = () => { };
|
||||||
@ -26,6 +39,7 @@ const Visualizer = (props) => {
|
|||||||
<Network
|
<Network
|
||||||
networkGraph={networkGraph}
|
networkGraph={networkGraph}
|
||||||
setRef={setFgRef}
|
setRef={setFgRef}
|
||||||
|
setSelection={props.setSelection}
|
||||||
/>
|
/>
|
||||||
<div className={"visualizer-controls"}>
|
<div className={"visualizer-controls"}>
|
||||||
<Button variant={"secondary"} size={"sm"} onClick={centerFocus}>Zoom to Fit</Button>
|
<Button variant={"secondary"} size={"sm"} onClick={centerFocus}>Zoom to Fit</Button>
|
||||||
|
8
ui/src/console/visualizer/selection.js
Normal file
8
ui/src/console/visualizer/selection.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const isSelectionGone = (networkGraph, selection) => {
|
||||||
|
// the selection is gone if the selection is not found in the network graph
|
||||||
|
return !networkGraph.nodes.find(node => selection.id === node.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const markSelected = (networkGraph, selection) => {
|
||||||
|
networkGraph.nodes.forEach(node => { node.selected = node.id === selection.id; });
|
||||||
|
}
|
@ -14,6 +14,14 @@ code, pre {
|
|||||||
padding: 15px 50px 20px;
|
padding: 15px 50px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-family: 'Russo One', sans-serif;
|
||||||
|
font-size: 3em;
|
||||||
|
margin-left: 0.53em;
|
||||||
|
vertical-align: center;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
.visualizer-container {
|
.visualizer-container {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: #3b2693;
|
background: #3b2693;
|
||||||
@ -25,12 +33,8 @@ code, pre {
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-title {
|
.detail-container {
|
||||||
font-family: 'Russo One', sans-serif;
|
margin-top: 15px;
|
||||||
font-size: 3em;
|
|
||||||
margin-left: 0.53em;
|
|
||||||
vertical-align: center;
|
|
||||||
line-height: 1.7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-close {
|
.btn-close {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user