Skip to content

Commit

Permalink
#7-tree-structure-view
Browse files Browse the repository at this point in the history
  • Loading branch information
tplocic20 committed Aug 26, 2024
1 parent 428e983 commit a6d9dc3
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 64 deletions.
2 changes: 0 additions & 2 deletions src/components/ClientAllocationPage/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useParams } from 'react-router-dom';

import { useFetch } from 'hooks/fetch';

import { PageHeading } from 'components/PageHeading';
import { TableHeading } from 'components/TableHeading';
import { Table } from 'components/Table';
Expand Down
1 change: 0 additions & 1 deletion src/components/DashboardPageV2/dataCapFlowGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ export const DataCapFlowGraph = () => {
});
skData = {...skData, ...sankeyLevel2(allocators, dc)};
})

return skData;
}

Expand Down
269 changes: 217 additions & 52 deletions src/components/DashboardPageV2/dataCapFlowTree.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,243 @@
import { useFetch } from '../../hooks/fetch';
import { convertBytesToIEC } from '../../utils/bytes';
import { useEffect, useMemo, useState } from 'react';
import { ResponsiveContainer, Sankey, Tooltip } from 'recharts';
import filesize from 'filesize';
import Tree from 'react-d3-tree';
import s from './s.module.css';
import { xFormatter } from '../../utils/chart';
import { ArrowLeft, ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import cn from 'classnames';
import Tree from 'react-d3-tree';

const PB_10 = 10 * 1024 * 1024 * 1024 * 1024 * 1024;
const PB_15 = 15 * 1024 * 1024 * 1024 * 1024 * 1024;

export const DataCapFlowTree = () => {
const fetchUrl = '/get-dc-flow-graph';

const fetchUrl = '/get-dc-flow-graph-grouped-by-audit-status';
const [data, { loading, loaded }] = useFetch(fetchUrl);
const [dimensions, setDimensions] = useState({
width: 0, height: 0
});
const [translation, setTranslation] = useState({
x: 0, y: 0
});
const [treeChartContainerRef, setTreeChartContainerRef] = useState(undefined);

const groupChildren = (children) => {
const render = (props) => (
<TreeNode
nodeDatum={props.nodeDatum}
toggleNode={props.toggleNode}
/>
);

}
const getName = (key) => {
switch (key) {
case 'rkh':
return 'Root Key Holder';
case 'inactiveAllocators':
return 'Inactive Allocators';
case 'activeAllocators':
return 'Active Allocators';
case 'passedAudit':
return 'Passed Audit';
case 'passedAuditConditionally':
return 'Passed Audit Conditionally';
case 'failedAudit':
return 'Failed Audit';
case 'notAudited':
return 'Not Audited';
default:
return key;
}
};

const treeData = useMemo(() => {
if (!data?.rkh) {
return null;
const countChildAllocators = (data) => {
if (data?.datacap) {
return undefined;
}

let skData = {
name: 'Root Key Holder',
children: [
{
name: '<10 PiB',
children: Object.entries(data.rkh).filter(([key, value]) => {
return value.datacap < PB_10
}).map(([key, value]) => ({
name: key,
children: []
}))
},
{
name: '>10 PiB & <15 PiB',
children: Object.entries(data.rkh).filter(([key, value]) => {
return value.datacap >= PB_10 && value.datacap < PB_15
}).map(([key, value]) => ({
name: key,
children: []
}))
}, {
name: '>15 PiB',
children: Object.entries(data.rkh).filter(([key, value]) => {
return value.datacap >= PB_15
}).map(([key, value]) => ({
name: key,
children: []
}))
}
]
};
if (data?.allocators?.length) {
return data.allocators.length;
} else {
return Object.entries(data).reduce((acc, [key, data]) => {
return acc + countChildAllocators(data);
}, 0);
}
};

console.log(data);
const groupAllocators = (allocators, skipUnique) => {

console.log('skData', skData)
const uniqueAllocationValues = [...new Set(allocators.map(a => a.datacap))];

return skData;
if (uniqueAllocationValues.length > 3 && !skipUnique) {
const datacapAllocatorsGrouped = Object.groupBy(Object.values(allocators), item => {
if (item.datacap < PB_10) {
return '<10 PiB';
} else if (item.datacap < PB_15) {
return '>10 PiB & <15 PiB';
} else {
return '>15 PiB';
}
});
return Object.entries(datacapAllocatorsGrouped).map(([key, data]) => {
return {
name: key,
attributes: {
datacap: convertBytesToIEC(data.reduce((acc, curr) => acc + +curr.datacap, 0)),
allocators: data.length
},
children: groupAllocators(data, true)
};
});
} else {
const datacapAllocatorsGrouped = Object.groupBy(Object.values(allocators), item => convertBytesToIEC(+item.datacap));

if (Object.keys(datacapAllocatorsGrouped).length === 1) {
return Object.values(datacapAllocatorsGrouped)[0].map((data) => {
return {
name: data.name ?? data.addressId,
attributes: {
datacap: convertBytesToIEC(+data.datacap),
id: data.addressId
},
children: undefined
};
});
}

return Object.entries(datacapAllocatorsGrouped).map(([key, data]) => {
if (data.length === 1) {
return {
name: data[0].name ?? data[0].addressId,
attributes: {
datacap: convertBytesToIEC(+data[0].datacap),
id: data[0].addressId
},
children: undefined
};
}
return {
name: key,
attributes: {
datacap: convertBytesToIEC(data.reduce((acc, curr) => acc + +curr.datacap, 0)),
allocators: data.length
},
children: data.map((data) => {
return {
name: data.name ?? data.addressId,
attributes: {
datacap: convertBytesToIEC(+data.datacap),
id: data.addressId
},
children: undefined
};
})
};
});
}
};

const parseChildren = (data) => {
if (data?.allocators?.length) {
if (data?.allocators?.length > 10) {
return groupAllocators(data.allocators);
}
return data.allocators.map((data) => {
return {
name: data.name ?? data.addressId,
attributes: {
datacap: convertBytesToIEC(+data.datacap),
id: data.addressId
},
children: undefined
};
});
} else {
return Object.entries(data).map(([key, data]) => {
return {
name: getName(key),
attributes: {
datacap: convertBytesToIEC(data.totalDc ? +data.totalDc : Object.values(data).reduce((acc, val) => acc + +val.totalDc, 0)),
allocators: countChildAllocators(data)
},
children: parseChildren(data)
};
});
}
};

const treeData = useMemo(() => {
return Object.entries(data).map(([key, data]) => {
return {
name: getName(key),
children: parseChildren(data)
};
});
}, [data]);

return <>
{treeData && <div>
<div id="treeWrapper" style={{ width: '100%', height: '1000px' }}>
<Tree data={treeData} />
useEffect(() => {
if (treeChartContainerRef?.getBoundingClientRect) {
const dimensions = treeChartContainerRef.getBoundingClientRect();
setDimensions(dimensions);
setTranslation({
x: dimensions.width / 5,
y: dimensions.height / 2
});
}
}, [treeChartContainerRef]);

return <div>
{!!treeData?.length && <div>
<div ref={(ref) => setTreeChartContainerRef(ref)} id="treeWrapper" style={{ width: '100%', height: '1000px' }}>
<Tree data={treeData}
initialDepth={1}
separation={{ siblings: 0.66, nonSiblings: 1 }}
nodeSize={{ x: 200, y: 200 }}
dimensions={dimensions}
translate={translation}
zoomable={false}
collapsible={true}
depthFactor={400}
renderCustomNodeElement={render}
shouldCollapseNeighborNodes={true} />
</div>
</div>}
</>;
</div>;
};


const TreeNode = ({ nodeDatum, toggleNode, onNodeClick }) => {
return (
<>
<circle className={!!nodeDatum.children?.length ? s.treeLabelNode : s.treeLabelNodeLeaf} r={15}
onClick={toggleNode}></circle>
<g className="rd3t-label">
<text
className={s.treeLabelTitle}
textAnchor="start"
fontWeight={400}
x="-20"
y="35"
>
{nodeDatum.name}
</text>
<text className={s.treeLabelInfo} x="-20" y="35">
{nodeDatum.attributes &&
Object.entries(nodeDatum.attributes).map(([labelKey, labelValue], i) => (
<tspan key={`${labelKey}-${i}`} x="-20" dy="1.2em">
{labelKey}: {labelValue}
</tspan>
))}
</text>
{!!nodeDatum?.attributes?.id && <text
className={cn(s.treeLabelTitle, s.treeLabelTitleHoverable)}
textAnchor="start"
fontWeight={400}
x="-20"
y="90"
onClick={() => {
window.open(`notaries\\${nodeDatum?.attributes?.id}`, '_blank');
}}
>
See details
</text>}
</g>
</>
);
};
24 changes: 15 additions & 9 deletions src/components/DashboardPageV2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import cn from 'classnames';
import { useFetch } from 'hooks/fetch';
import { convertBytesToIEC } from 'utils/bytes';
import s from './s.module.css';
import { useState } from 'react';
import { useCallback, useState } from 'react';
import { Link } from 'react-router-dom';
import { ArrowRight } from 'lucide-react';
import { DataCapAllocVsAvailable } from './dataCapAllocVsAvailable';
Expand All @@ -12,10 +12,12 @@ import { DataCapFlowGraph } from './dataCapFlowGraph';
import { DataCapFlowTree } from './dataCapFlowTree';
import { LoadingValue } from '../LoadingValue';
import { ContentTabs } from '../ContentTabs';
import { useScrollToHash } from '../../hooks/useScrollToHash';

export default function DashboardPage() {
const fetchUrl = '/getFilPlusStats';
const [toggle, setToggle] = useState(false);
const scrollToHash = useScrollToHash();

const [data, { loading, loaded }] = useFetch(fetchUrl);

Expand All @@ -24,9 +26,16 @@ export default function DashboardPage() {
numberOfActiveNotariesV2,
numberOfAllocators,
totalDcGivenToAllocators,
totalDcUsedByAllocators,
totalDcUsedByAllocators
} = data;

const toggleChartsWrapper = useCallback(() => {
if (!toggle) {
scrollToHash('chartsWrapper');
}
setToggle(!toggle);
}, [toggle, scrollToHash]);

return (
<div className="container">
<h2 className="h1">State of Fil+</h2>
Expand Down Expand Up @@ -98,23 +107,20 @@ export default function DashboardPage() {
</div>
</div>
<div className="grid">
<div className={cn("card", toggle ? "size6" : "size4")}>
<div id="chartsWrapper" className={cn('card', toggle ? 'size6' : 'size4')}>
<div className="cardTitle noMargin">
<span>System Structure</span>
<button className={s.cardButton} onClick={() => setToggle(!toggle)}>
<button className={s.cardButton} onClick={toggleChartsWrapper}>
<ArrowRight
size={18}
style={{ transform: toggle ? 'rotate(90deg' : '' }}
/>
</button>
</div>
<div className={cn(s.cardData, !toggle && s.cardDataHidden)}>
<ContentTabs tabs={['Flow chart', 'Allocation tree']}>
<ContentTabs tabs={['Allocators tree', 'DataCap flow']}>
<DataCapFlowTree />
<DataCapFlowGraph />
<div style={{padding: '2em'}}>
Coming soon...
</div>
{/*<DataCapFlowTree />*/}
</ContentTabs>
</div>
</div>
Expand Down
Loading

0 comments on commit a6d9dc3

Please sign in to comment.