Updated journey counts.

This commit is contained in:
Mike Cao 2024-06-13 22:15:31 -07:00
parent 3234120bfb
commit 1463482e89
2 changed files with 116 additions and 94 deletions

View file

@ -147,7 +147,6 @@
} }
.end { .end {
right: 0;
width: 50px; width: 50px;
height: 30px; height: 30px;
border: 0; border: 0;
@ -161,7 +160,7 @@
} }
.up .end { .up .end {
width: 53px; width: 52px;
bottom: 27px; bottom: 27px;
right: 0; right: 0;
border-bottom-left-radius: 100%; border-bottom-left-radius: 100%;
@ -177,7 +176,7 @@
} }
.down .end { .down .end {
width: 53px; width: 52px;
top: 30px; top: 30px;
right: 0; right: 0;
border-top-left-radius: 100%; border-top-left-radius: 100%;
@ -186,16 +185,15 @@
} }
.flat .start { .flat .start {
left: 0;
top: 30px; top: 30px;
width: 50px;
border-top: 3px solid var(--journey-line-color); border-top: 3px solid var(--journey-line-color);
} }
.flat .end { .flat .end {
right: 0; right: 0;
bottom: 27px; top: 30px;
width: 50px; border-top: 3px solid var(--journey-line-color);
border-bottom: 3px solid var(--journey-line-color);
} }
.start:before, .start:before,
@ -214,14 +212,12 @@
display: none; display: none;
} }
.up .start:before, .up .start:before {
.flat .start:before {
left: -8px; left: -8px;
top: -8px; top: -8px;
} }
.up .end:before, .up .end:before {
.flat .end:before {
right: -8px; right: -8px;
bottom: -8px; bottom: -8px;
} }
@ -236,6 +232,16 @@
top: -8px; top: -8px;
} }
.flat .start:before {
left: -8px;
top: -8px;
}
.flat .end:before {
right: -8px;
top: -8px;
}
.line.active .segment, .line.active .segment,
.line.active .segment:before { .line.active .segment:before {
border-color: var(--journey-active-color); border-color: var(--journey-active-color);

View file

@ -23,61 +23,69 @@ export default function JourneyView() {
if (!data) { if (!data) {
return []; return [];
} }
const selectedPaths = selectedNode?.paths ?? [];
const activePaths = activeNode?.paths ?? [];
return Array(Number(parameters.steps)) return Array(Number(parameters.steps))
.fill(undefined) .fill(undefined)
.map((column = {}, index) => { .map((nodes = {}, index) => {
data.forEach(({ items, count }) => { data.forEach(({ items, count }) => {
const name = items[index]; const name = items[index];
const selectedNodes = selectedNode?.paths ?? [];
if (name) { if (name) {
const selected = !!selectedNodes.find(a => a.items[index] === name); const selected = !!selectedPaths.find(path => path.items[index] === name);
if (!column[name]) { const active = selected && !!activePaths.find(path => path.items[index] === name);
if (!nodes[name]) {
const paths = data.filter((d, i) => { const paths = data.filter((d, i) => {
return i !== index && d.items[index] === name; return i !== index && d.items[index] === name;
}); });
const from = const from =
index > 0 && index > 0 &&
selected && selected &&
paths.reduce((obj, path) => { paths.reduce((obj, path) => {
const { items, count } = path; const { items, count } = path;
const name = items[index - 1]; const name = items[index - 1];
if (!obj[name]) { if (!obj[name]) {
obj[name] = { name, count }; obj[name] = { name, count };
} else { } else {
obj[name].count += count; obj[name].count += count;
} }
return obj; return obj;
}, {}); }, {});
column[name] = { nodes[name] = {
name, name,
count, count,
total: count, total: count,
columnIndex: index, columnIndex: index,
selected, selected,
selectedCount: count, active,
paths, paths,
from: objectToArray(from), from: objectToArray(from),
}; };
} else { } else {
column[name].selectedCount += selected ? count : 0; nodes[name].total += count;
column[name].total += count;
} }
} }
}); });
return { return {
nodes: objectToArray(column).sort(firstBy('total', -1)), nodes: objectToArray(nodes).sort(firstBy('total', -1)),
}; };
}); });
}, [data, selectedNode]); }, [data, selectedNode, activeNode]);
const handleClick = (item: string, index: number, paths: any[]) => { const handleClick = (name: string, index: number, paths: any[]) => {
if (item !== selectedNode?.item || index !== selectedNode?.index) { if (name !== selectedNode?.name || index !== selectedNode?.index) {
setSelectedNode({ item, index, paths }); setSelectedNode({ name, index, paths });
} else { } else {
setSelectedNode(null); setSelectedNode(null);
setActiveNode(null);
} }
}; };
@ -85,7 +93,7 @@ export default function JourneyView() {
return null; return null;
} }
//console.log({ columns, selectedNode, activeNode }); //console.log({ data, columns, selectedNode, activeNode });
return ( return (
<div className={styles.container}> <div className={styles.container}>
@ -100,81 +108,89 @@ export default function JourneyView() {
<div className={styles.num}>{columnIndex + 1}</div> <div className={styles.num}>{columnIndex + 1}</div>
</div> </div>
<div className={styles.nodes}> <div className={styles.nodes}>
{column.nodes.map( {column.nodes.map(({ name, total, selected, active, paths, from }, nodeIndex) => {
({ name, total, selected, paths, from, selectedCount }, nodeIndex) => { const previousNodes = columns[columnIndex - 1]?.nodes;
const active = let selectedCount = from?.length ? 0 : total;
selected && activeNode?.paths.find(path => path.items[columnIndex] === name); let activeCount = selectedCount;
const lines = from?.reduce((arr, { name }: any) => { const lines = from?.reduce((arr, { name, count }: any) => {
const fromIndex = columns[columnIndex - 1]?.nodes.findIndex(node => { const fromIndex = previousNodes.findIndex(node => {
return node.name === name && node.selected; return node.name === name && node.selected;
}); });
if (fromIndex > -1) { if (fromIndex > -1) {
arr.push([fromIndex, nodeIndex]); arr.push([fromIndex, nodeIndex]);
} selectedCount += count;
}
return arr; if (
}, []); previousNodes.findIndex(node => {
return node.name === name && node.active;
}) > -1
) {
activeCount += count;
}
return ( return arr;
<div }, []);
key={name}
className={classNames(styles.item, {
[styles.selected]: selected,
[styles.active]: active,
})}
onClick={() => handleClick(name, columnIndex, paths)}
onMouseEnter={() => selected && setActiveNode({ name, columnIndex, paths })}
onMouseLeave={() => selected && setActiveNode(null)}
>
<div className={styles.name}>{name}</div>
<div className={styles.count}>
{selected || active ? selectedCount : total}
</div>
{columnIndex < columns.length &&
lines.map(([fromIndex, nodeIndex], i) => {
const height =
(Math.abs(nodeIndex - fromIndex) + 1) * (NODE_HEIGHT + NODE_GAP) -
NODE_GAP;
const midHeight =
(Math.abs(nodeIndex - fromIndex) - 1) * (NODE_HEIGHT + NODE_GAP) +
NODE_GAP +
LINE_WIDTH;
const nodeName = columns[columnIndex - 1]?.nodes[fromIndex].name;
return ( return (
<div <div
key={`${fromIndex}${nodeIndex}${i}`} key={name}
className={classNames(styles.line, { className={classNames(styles.item, {
[styles.active]: [styles.selected]: selected,
active && [styles.active]: active,
activeNode?.paths.find( })}
path => onClick={() => handleClick(name, columnIndex, paths)}
path.items[columnIndex] === name && onMouseEnter={() => selected && setActiveNode({ name, columnIndex, paths })}
path.items[columnIndex - 1] === nodeName, onMouseLeave={() => selected && setActiveNode(null)}
), >
[styles.up]: fromIndex < nodeIndex, <div className={styles.name}>{name}</div>
[styles.down]: fromIndex > nodeIndex, <div className={styles.count}>
[styles.flat]: fromIndex === nodeIndex, {selected ? (active ? activeCount : selectedCount) : total}
})}
style={{ height }}
>
<div className={classNames(styles.segment, styles.start)} />
<div
className={classNames(styles.segment, styles.mid)}
style={{
height: midHeight,
}}
/>
<div className={classNames(styles.segment, styles.end)} />
</div>
);
})}
</div> </div>
); {columnIndex < columns.length &&
}, lines.map(([fromIndex, nodeIndex], i) => {
)} const height =
(Math.abs(nodeIndex - fromIndex) + 1) * (NODE_HEIGHT + NODE_GAP) -
NODE_GAP;
const midHeight =
(Math.abs(nodeIndex - fromIndex) - 1) * (NODE_HEIGHT + NODE_GAP) +
NODE_GAP +
LINE_WIDTH;
const nodeName = columns[columnIndex - 1]?.nodes[fromIndex].name;
return (
<div
key={`${fromIndex}${nodeIndex}${i}`}
className={classNames(styles.line, {
[styles.active]:
active &&
activeNode?.paths.find(
path =>
path.items[columnIndex] === name &&
path.items[columnIndex - 1] === nodeName,
),
[styles.up]: fromIndex < nodeIndex,
[styles.down]: fromIndex > nodeIndex,
[styles.flat]: fromIndex === nodeIndex,
})}
style={{ height }}
>
<div className={classNames(styles.segment, styles.start)} />
<div
className={classNames(styles.segment, styles.mid)}
style={{
height: midHeight,
}}
/>
<div className={classNames(styles.segment, styles.end)} />
</div>
);
})}
</div>
);
})}
</div> </div>
</div> </div>
); );