datajunction-ui
Version:
DataJunction Metrics Platform UI
154 lines (147 loc) • 4.58 kB
JSX
import { useEffect, useState } from 'react';
import * as React from 'react';
import { labelize } from '../../../utils/form';
import LoadingIcon from '../../icons/LoadingIcon';
export default function NodeDependenciesTab({ node, djClient }) {
const [nodeDAG, setNodeDAG] = useState({
upstreams: [],
downstreams: [],
dimensions: [],
});
const [retrieved, setRetrieved] = useState(false);
useEffect(() => {
const fetchData = async () => {
if (node) {
let upstreams = await djClient.upstreams(node.name);
let downstreams = await djClient.downstreams(node.name);
let dimensions = await djClient.nodeDimensions(node.name);
setNodeDAG({
upstreams: upstreams,
downstreams: downstreams,
dimensions: dimensions,
});
setRetrieved(true);
}
};
fetchData().catch(console.error);
}, [djClient, node]);
// Builds the block of dimensions selectors, grouped by node name + path
return (
<div>
<h2>Upstreams</h2>
{retrieved ? (
<NodeList nodes={nodeDAG.upstreams} />
) : (
<span style={{ display: 'block' }}>
<LoadingIcon centered={false} />
</span>
)}
<h2>Downstreams</h2>
{retrieved ? (
<NodeList nodes={nodeDAG.downstreams} />
) : (
<span style={{ display: 'block' }}>
<LoadingIcon centered={false} />
</span>
)}
<h2>Dimensions</h2>
{retrieved ? (
<NodeDimensionsList rawDimensions={nodeDAG.dimensions} />
) : (
<span style={{ display: 'block' }}>
<LoadingIcon centered={false} />
</span>
)}
</div>
);
}
export function NodeDimensionsList({ rawDimensions }) {
const dimensions = Object.entries(
rawDimensions.reduce((group, dimension) => {
group[dimension.node_name + dimension.path] =
group[dimension.node_name + dimension.path] ?? [];
group[dimension.node_name + dimension.path].push(dimension);
return group;
}, {}),
);
return (
<div style={{ padding: '0.5rem' }}>
{dimensions.map(grouping => {
const dimensionsInGroup = grouping[1];
const role = dimensionsInGroup[0].path
.map(pathItem => pathItem.split('.').slice(-1))
.join(' → ');
const fullPath = dimensionsInGroup[0].path.join(' → ');
const groupHeader = (
<span
style={{
fontWeight: 'normal',
marginBottom: '15px',
marginTop: '15px',
}}
>
<a href={`/nodes/${dimensionsInGroup[0].node_name}`}>
<b>{dimensionsInGroup[0].node_display_name}</b>
</a>{' '}
with role{' '}
<span className="HighlightPath">
<b>{role}</b>
</span>{' '}
via <span className="HighlightPath">{fullPath}</span>
</span>
);
const dimensionGroupOptions = dimensionsInGroup.map(dim => {
return {
value: dim.name,
label:
labelize(dim.name.split('.').slice(-1)[0]) +
(dim.properties.includes('primary_key') ? ' (PK)' : ''),
};
});
return (
<details key={grouping[0]}>
<summary style={{ marginBottom: '10px' }}>{groupHeader}</summary>
<div className="dimensionsList">
{dimensionGroupOptions.map(dimension => {
return (
<div key={dimension.value}>
{dimension.label.split('[').slice(0)[0]} ⇢{' '}
<code className="DimensionAttribute">
{dimension.value}
</code>
</div>
);
})}
</div>
</details>
);
})}
</div>
);
}
export function NodeList({ nodes }) {
return nodes && nodes.length > 0 ? (
<ul className="backfills">
{nodes?.map(node => (
<li
className="backfill"
style={{ marginBottom: '5px' }}
key={node.name}
>
<span
className={`node_type__${node.type} badge node_type`}
style={{ marginRight: '5px' }}
role="dialog"
aria-hidden="false"
aria-label="NodeType"
>
{node.type}
</span>
<a href={`/nodes/${node.name}`}>{node.name}</a>
</li>
))}
</ul>
) : (
<span style={{ display: 'block' }}>None</span>
);
}