2024-06-01 18:45:06 +00:00
|
|
|
import { useContext, useMemo, useState } from 'react';
|
2024-05-17 08:42:36 +00:00
|
|
|
import { ReportContext } from '../[reportId]/Report';
|
2024-06-01 18:45:06 +00:00
|
|
|
import { firstBy } from 'thenby';
|
|
|
|
|
import styles from './JourneyView.module.css';
|
|
|
|
|
import classNames from 'classnames';
|
|
|
|
|
import { useEscapeKey } from 'components/hooks';
|
2024-05-17 08:42:36 +00:00
|
|
|
|
|
|
|
|
export default function JourneyView() {
|
2024-06-01 18:45:06 +00:00
|
|
|
const [selected, setSelected] = useState(null);
|
2024-05-17 08:42:36 +00:00
|
|
|
const { report } = useContext(ReportContext);
|
|
|
|
|
const { data } = report || {};
|
2024-06-01 18:45:06 +00:00
|
|
|
useEscapeKey(() => setSelected(null));
|
|
|
|
|
|
|
|
|
|
const columns = useMemo(() => {
|
|
|
|
|
if (!data) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
return Array(data[0].items.length)
|
|
|
|
|
.fill(undefined)
|
|
|
|
|
.map((col = {}, index) => {
|
|
|
|
|
data.forEach(({ items, count }) => {
|
|
|
|
|
const item = items[index];
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
if (!col[item]) {
|
|
|
|
|
col[item] = {
|
|
|
|
|
item,
|
|
|
|
|
total: +count,
|
|
|
|
|
index,
|
|
|
|
|
paths: [
|
|
|
|
|
data.filter((d, i) => {
|
|
|
|
|
return d.items[index] === item && i !== index;
|
|
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
col[item].total += +count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return Object.keys(col)
|
|
|
|
|
.map(key => col[key])
|
|
|
|
|
.sort(firstBy('total', -1));
|
|
|
|
|
});
|
|
|
|
|
}, [data]);
|
|
|
|
|
|
|
|
|
|
const handleClick = (item: string, index: number, paths: any[]) => {
|
|
|
|
|
if (item !== selected?.item || index !== selected?.index) {
|
|
|
|
|
setSelected({ item, index, paths });
|
|
|
|
|
} else {
|
|
|
|
|
setSelected(null);
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-05-17 08:42:36 +00:00
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-01 18:45:06 +00:00
|
|
|
return (
|
|
|
|
|
<div className={styles.container}>
|
|
|
|
|
<div className={styles.view}>
|
|
|
|
|
{columns.map((column, index) => {
|
|
|
|
|
const current = index === selected?.index;
|
|
|
|
|
const behind = index <= selected?.index - 1;
|
|
|
|
|
const ahead = index > selected?.index;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={index}
|
|
|
|
|
className={classNames(styles.column, {
|
|
|
|
|
[styles.current]: current,
|
|
|
|
|
[styles.behind]: behind,
|
|
|
|
|
[styles.ahead]: ahead,
|
|
|
|
|
})}
|
|
|
|
|
>
|
|
|
|
|
<div className={styles.header}>
|
|
|
|
|
<div className={styles.num}>{index + 1}</div>
|
|
|
|
|
</div>
|
|
|
|
|
{column.map(({ item, total, paths }) => {
|
|
|
|
|
const highlight = selected?.paths.find(arr => {
|
|
|
|
|
return arr.find(a => a.items[index] === item);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={item}
|
|
|
|
|
className={classNames(styles.item, {
|
|
|
|
|
[styles.highlight]: highlight,
|
|
|
|
|
})}
|
|
|
|
|
onClick={() => handleClick(item, index, paths)}
|
|
|
|
|
>
|
|
|
|
|
{item} ({total})
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2024-05-17 08:42:36 +00:00
|
|
|
}
|