mirror of
https://github.com/ansible/awx.git
synced 2026-05-13 12:27:37 -02:30
Update layout; fix multiple renders happening on page load.
This commit is contained in:
@@ -1,107 +1,89 @@
|
|||||||
import React, { useEffect, useCallback } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import debounce from 'util/debounce';
|
import debounce from 'util/debounce';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
// import { t } from '@lingui/macro';
|
// import { t } from '@lingui/macro';
|
||||||
import { InstancesAPI } from 'api';
|
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
function MeshGraph({ data }) {
|
// function MeshGraph({ data }) {
|
||||||
// function MeshGraph() {
|
function MeshGraph({ redirectToDetailsPage }) {
|
||||||
// const data = {
|
|
||||||
// nodes: [
|
|
||||||
// {
|
|
||||||
// hostname: 'aapc1.local',
|
|
||||||
// node_state: 'healthy',
|
|
||||||
// node_type: 'control',
|
|
||||||
// id: 1,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aapc2.local',
|
|
||||||
// node_type: 'control',
|
|
||||||
// node_state: 'disabled',
|
|
||||||
// id: 2,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aapc3.local',
|
|
||||||
// node_type: 'control',
|
|
||||||
// node_state: 'healthy',
|
|
||||||
// id: 3,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aape1.local',
|
|
||||||
// node_type: 'execution',
|
|
||||||
// node_state: 'error',
|
|
||||||
// id: 4,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aape2.local',
|
|
||||||
// node_type: 'execution',
|
|
||||||
// node_state: 'error',
|
|
||||||
// id: 5,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aape3.local',
|
|
||||||
// node_type: 'execution',
|
|
||||||
// node_state: 'healthy',
|
|
||||||
// id: 6,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aape4.local',
|
|
||||||
// node_type: 'execution',
|
|
||||||
// node_state: 'healthy',
|
|
||||||
// id: 7,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aaph1.local',
|
|
||||||
// node_type: 'hop',
|
|
||||||
// node_state: 'disabled',
|
|
||||||
// id: 8,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aaph2.local',
|
|
||||||
// node_type: 'hop',
|
|
||||||
// node_state: 'healthy',
|
|
||||||
// id: 9,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hostname: 'aaph3.local',
|
|
||||||
// node_type: 'hop',
|
|
||||||
// node_state: 'error',
|
|
||||||
// id: 10,
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// links: [
|
|
||||||
// { source: 'aapc1.local', target: 'aapc2.local' },
|
|
||||||
// { source: 'aapc1.local', target: 'aapc3.local' },
|
|
||||||
// { source: 'aapc1.local', target: 'aape1.local' },
|
|
||||||
// { source: 'aapc1.local', target: 'aape2.local' },
|
|
||||||
|
|
||||||
// { source: 'aapc2.local', target: 'aapc3.local' },
|
|
||||||
// { source: 'aapc2.local', target: 'aape1.local' },
|
|
||||||
// { source: 'aapc2.local', target: 'aape2.local' },
|
|
||||||
|
|
||||||
// { source: 'aapc3.local', target: 'aape1.local' },
|
|
||||||
// { source: 'aapc3.local', target: 'aape2.local' },
|
|
||||||
|
|
||||||
// { source: 'aape3.local', target: 'aaph1.local' },
|
|
||||||
// { source: 'aape3.local', target: 'aaph2.local' },
|
|
||||||
|
|
||||||
// { source: 'aape4.local', target: 'aaph3.local' },
|
|
||||||
|
|
||||||
// { source: 'aaph1.local', target: 'aapc1.local' },
|
|
||||||
// { source: 'aaph1.local', target: 'aapc2.local' },
|
|
||||||
// { source: 'aaph1.local', target: 'aapc3.local' },
|
|
||||||
|
|
||||||
// { source: 'aaph2.local', target: 'aapc1.local' },
|
|
||||||
// { source: 'aaph2.local', target: 'aapc2.local' },
|
|
||||||
// { source: 'aaph2.local', target: 'aapc3.local' },
|
|
||||||
|
|
||||||
// { source: 'aaph3.local', target: 'aaph1.local' },
|
|
||||||
// { source: 'aaph3.local', target: 'aaph2.local' },
|
|
||||||
// ],
|
|
||||||
// };
|
|
||||||
const history = useHistory();
|
|
||||||
const draw = useCallback(() => {
|
const draw = useCallback(() => {
|
||||||
|
const data = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
hostname: 'awx_1',
|
||||||
|
node_type: 'hybrid',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
hostname: 'receptor-1',
|
||||||
|
node_type: 'execution',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
hostname: 'receptor-2',
|
||||||
|
node_type: 'execution',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
hostname: 'receptor-hop',
|
||||||
|
node_type: 'hop',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
hostname: 'receptor-hop-1',
|
||||||
|
node_type: 'hop',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
hostname: 'receptor-hop-2',
|
||||||
|
node_type: 'hop',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
hostname: 'receptor-hop-3',
|
||||||
|
node_type: 'hop',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
hostname: 'receptor-hop-4',
|
||||||
|
node_type: 'hop',
|
||||||
|
node_state: 'healthy',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
source: 'receptor-hop',
|
||||||
|
target: 'awx_1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'receptor-1',
|
||||||
|
target: 'receptor-hop',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'receptor-2',
|
||||||
|
target: 'receptor-hop',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'receptor-hop-3',
|
||||||
|
target: 'receptor-hop',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// "source": "receptor-2",
|
||||||
|
// "target": "receptor-hop-1"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "source": "receptor-2",
|
||||||
|
// "target": "receptor-hop-2"
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
};
|
||||||
const margin = 80;
|
const margin = 80;
|
||||||
const getWidth = () => {
|
const getWidth = () => {
|
||||||
let width;
|
let width;
|
||||||
@@ -124,7 +106,7 @@ function MeshGraph({ data }) {
|
|||||||
|
|
||||||
const zoom = d3
|
const zoom = d3
|
||||||
.zoom()
|
.zoom()
|
||||||
.scaleExtent([1, 8])
|
// .scaleExtent([1, 8])
|
||||||
.on('zoom', (event) => {
|
.on('zoom', (event) => {
|
||||||
svg.attr('transform', event.transform);
|
svg.attr('transform', event.transform);
|
||||||
});
|
});
|
||||||
@@ -145,27 +127,41 @@ function MeshGraph({ data }) {
|
|||||||
|
|
||||||
const simulation = d3
|
const simulation = d3
|
||||||
.forceSimulation()
|
.forceSimulation()
|
||||||
|
.force('charge', d3.forceManyBody(75).strength(-100))
|
||||||
.force(
|
.force(
|
||||||
'link',
|
'link',
|
||||||
d3.forceLink().id((d) => d.hostname)
|
d3.forceLink().id((d) => d.hostname)
|
||||||
)
|
)
|
||||||
.force('charge', d3.forceManyBody().strength(-350))
|
.force('collide', d3.forceCollide(80))
|
||||||
.force(
|
.force('forceX', d3.forceX(0))
|
||||||
'collide',
|
.force('forceY', d3.forceY(0))
|
||||||
d3.forceCollide((d) =>
|
|
||||||
d.node_type === 'execution' || d.node_type === 'hop' ? 75 : 100
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.force('center', d3.forceCenter(width / 2, height / 2));
|
.force('center', d3.forceCenter(width / 2, height / 2));
|
||||||
|
|
||||||
|
// const simulation = d3
|
||||||
|
// .forceSimulation()
|
||||||
|
// .force(
|
||||||
|
// 'link',
|
||||||
|
// d3.forceLink().id((d) => d.hostname)
|
||||||
|
// )
|
||||||
|
// .force('charge', d3.forceManyBody().strength(-350))
|
||||||
|
// .force(
|
||||||
|
// 'collide',
|
||||||
|
// d3.forceCollide((d) =>
|
||||||
|
// d.node_type === 'execution' || d.node_type === 'hop' ? 75 : 100
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// .force('center', d3.forceCenter(width / 2, height / 2));
|
||||||
|
|
||||||
const link = svg
|
const link = svg
|
||||||
.append('g')
|
.append('g')
|
||||||
.attr('class', `links`)
|
.attr('class', `links`)
|
||||||
.attr('data-cy', 'links')
|
.attr('data-cy', 'links')
|
||||||
.selectAll('path')
|
// .selectAll('path')
|
||||||
|
.selectAll('line')
|
||||||
.data(graph.links)
|
.data(graph.links)
|
||||||
.enter()
|
.enter()
|
||||||
.append('path')
|
.append('line')
|
||||||
|
// .append('path')
|
||||||
.attr('class', (d, i) => `link-${i}`)
|
.attr('class', (d, i) => `link-${i}`)
|
||||||
.attr('data-cy', (d) => `${d.source}-${d.target}`)
|
.attr('data-cy', (d) => `${d.source}-${d.target}`)
|
||||||
.style('fill', 'none')
|
.style('fill', 'none')
|
||||||
@@ -285,17 +281,22 @@ function MeshGraph({ data }) {
|
|||||||
simulation.force('link').links(graph.links);
|
simulation.force('link').links(graph.links);
|
||||||
|
|
||||||
function ticked() {
|
function ticked() {
|
||||||
link.attr('d', linkArc);
|
// link.attr('d', linkArc);
|
||||||
|
link
|
||||||
|
.attr('x1', (d) => d.source.x)
|
||||||
|
.attr('y1', (d) => d.source.y)
|
||||||
|
.attr('x2', (d) => d.target.x)
|
||||||
|
.attr('y2', (d) => d.target.y);
|
||||||
|
|
||||||
node.attr('transform', (d) => `translate(${d.x},${d.y})`);
|
node.attr('transform', (d) => `translate(${d.x},${d.y})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkArc(d) {
|
// function linkArc(d) {
|
||||||
const dx = d.target.x - d.source.x;
|
// const dx = d.target.x - d.source.x;
|
||||||
const dy = d.target.y - d.source.y;
|
// const dy = d.target.y - d.source.y;
|
||||||
const dr = Math.sqrt(dx * dx + dy * dy);
|
// const dr = Math.sqrt(dx * dx + dy * dy);
|
||||||
return `M${d.source.x},${d.source.y}A${dr},${dr} 0 0,1 ${d.target.x},${d.target.y}`;
|
// return `M${d.source.x},${d.source.y}A${dr},${dr} 0 0,1 ${d.target.x},${d.target.y}`;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function contractGlow() {
|
function contractGlow() {
|
||||||
svg
|
svg
|
||||||
@@ -329,7 +330,7 @@ function MeshGraph({ data }) {
|
|||||||
hop: '#C46100',
|
hop: '#C46100',
|
||||||
execution: '#F0AB00',
|
execution: '#F0AB00',
|
||||||
hybrid: '#0066CC',
|
hybrid: '#0066CC',
|
||||||
control: '#005F60'
|
control: '#005F60',
|
||||||
};
|
};
|
||||||
|
|
||||||
return colorKey[nodeType];
|
return colorKey[nodeType];
|
||||||
@@ -377,18 +378,7 @@ function MeshGraph({ data }) {
|
|||||||
.attr('r', defaultRadius);
|
.attr('r', defaultRadius);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
async function redirectToDetailsPage({ id: nodeId }) {
|
|
||||||
const {
|
|
||||||
data: { results },
|
|
||||||
} = await InstancesAPI.readInstanceGroup(nodeId);
|
|
||||||
const { id: instanceGroupId } = results[0];
|
|
||||||
const constructedURL = `/instance_groups/${instanceGroupId}/instances/${nodeId}/details`;
|
|
||||||
history.push(constructedURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
@@ -396,9 +386,7 @@ function MeshGraph({ data }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', debounce(handleResize, 500));
|
window.addEventListener('resize', debounce(handleResize, 500));
|
||||||
|
draw();
|
||||||
handleResize();
|
|
||||||
|
|
||||||
return () => window.removeEventListener('resize', handleResize);
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
}, [draw]);
|
}, [draw]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useEffect, useCallback } from 'react';
|
import React, { useEffect, useCallback } from 'react';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import ScreenHeader from 'components/ScreenHeader/ScreenHeader';
|
import ScreenHeader from 'components/ScreenHeader/ScreenHeader';
|
||||||
import { PageSection, Card, CardBody } from '@patternfly/react-core';
|
import { PageSection, Card, CardBody } from '@patternfly/react-core';
|
||||||
import useRequest from 'hooks/useRequest';
|
import useRequest from 'hooks/useRequest';
|
||||||
import { MeshAPI } from 'api';
|
import { MeshAPI, InstancesAPI } from 'api';
|
||||||
import MeshGraph from './MeshGraph';
|
import MeshGraph from './MeshGraph';
|
||||||
|
|
||||||
function TopologyView() {
|
function TopologyView() {
|
||||||
@@ -21,6 +22,15 @@ function TopologyView() {
|
|||||||
}, []),
|
}, []),
|
||||||
{ meshData: { nodes: [], links: [] } }
|
{ meshData: { nodes: [], links: [] } }
|
||||||
);
|
);
|
||||||
|
async function RedirectToDetailsPage({ id: nodeId }) {
|
||||||
|
const history = useHistory();
|
||||||
|
const {
|
||||||
|
data: { results },
|
||||||
|
} = await InstancesAPI.readInstanceGroup(nodeId);
|
||||||
|
const { id: instanceGroupId } = results[0];
|
||||||
|
const constructedURL = `/instance_groups/${instanceGroupId}/instances/${nodeId}/details`;
|
||||||
|
history.push(constructedURL);
|
||||||
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMeshVisualizer();
|
fetchMeshVisualizer();
|
||||||
}, [fetchMeshVisualizer]);
|
}, [fetchMeshVisualizer]);
|
||||||
@@ -30,7 +40,14 @@ function TopologyView() {
|
|||||||
|
|
||||||
<PageSection>
|
<PageSection>
|
||||||
<Card>
|
<Card>
|
||||||
<CardBody>{meshData && <MeshGraph data={meshData} />}</CardBody>
|
<CardBody>
|
||||||
|
{meshData && (
|
||||||
|
<MeshGraph
|
||||||
|
data={meshData}
|
||||||
|
redirectToDetailsPage={RedirectToDetailsPage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user