Skip to content

Commit c5a7e87

Browse files
Use MultiSelect in Volume Graph
1 parent 218c3ef commit c5a7e87

File tree

3 files changed

+123
-60
lines changed

3 files changed

+123
-60
lines changed

apps/frontend/src/components/bookkeeper/Volume/VolumeGraph/VolumeGraph.scss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,27 @@
33
.volume-graph {
44
width: 100%;
55
height: 100%;
6+
.volume-graph-container {
7+
.left-column {
8+
width: 25%;
9+
padding: 0.5rem;
10+
pointer-events: 'auto';
11+
z-index: 1000;
12+
}
13+
14+
.right-column {
15+
width: 75%;
16+
padding: 0.5rem;
17+
}
18+
19+
@media (max-width: 767.98px) {
20+
flex-direction: column;
21+
.left-column,
22+
.right-column {
23+
width: 100% !important;
24+
}
25+
}
26+
}
627
}
728

829
.recharts-wrapper {

apps/frontend/src/components/bookkeeper/Volume/VolumeGraph/VolumeGraph.tsx

Lines changed: 102 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useMemo, useState, useEffect, memo } from 'react';
2-
import { ResponsiveContainer, Cell, PieChart, Pie, Tooltip, Legend } from 'recharts';
31
import './VolumeGraph.scss';
2+
import { useMemo, useState, useEffect, memo, useCallback } from 'react';
3+
import { ResponsiveContainer, Cell, PieChart, Pie, Tooltip, Legend } from 'recharts';
44
import { getBarColors, TOTAL_LABELS, Units } from '../../../../utilities/constants';
55
import { formatCurrency } from '../../../../utilities/data-formatters';
66
import { transformVolumeGraphData } from '../../../../services/data-transform.service';
@@ -9,6 +9,7 @@ import { useLocation } from 'react-router-dom';
99
import { useSelector } from 'react-redux';
1010
import { selectIsDarkMode, selectUIConfigUnit } from '../../../../store/rootSelectors';
1111
import { selectVolumeForwards } from '../../../../store/bkprSelectors';
12+
import MultiSelectDropdown from '../../../shared/MultiSelectDropdown/MultiSelectDropdown';
1213

1314
const VolumeGraphTooltip = ({ active, payload, unit }: any) => {
1415
if (active && payload && payload.length >= 0) {
@@ -70,6 +71,23 @@ const VolumeGraph = () => {
7071
const uiConfigUnit = useSelector(selectUIConfigUnit);
7172
const volumeForwards = useSelector(selectVolumeForwards);
7273
const [animate, setAnimate] = useState(false);
74+
const [selectedScids, setSelectedScids] = useState<string[]>([]);
75+
const [filterMode, setFilterMode] = useState<string>('include');
76+
77+
const uniqueInChannelSCIDOptions = useMemo(() => (
78+
[...new Set(volumeForwards?.map(forward => forward.in_channel_scid) ?? [])]
79+
.map(scid => ({ value: scid, label: scid }))
80+
), [volumeForwards]);
81+
82+
const filteredVolumeForwards = useMemo(() => {
83+
if (!volumeForwards) return [];
84+
if (selectedScids.length === 0) return volumeForwards;
85+
86+
return volumeForwards.filter(forward => {
87+
const isMatch = selectedScids.includes(forward.in_channel_scid);
88+
return filterMode === 'include' ? isMatch : !isMatch;
89+
});
90+
}, [volumeForwards, selectedScids, filterMode]);
7391

7492
useEffect(() => {
7593
setAnimate(false);
@@ -83,15 +101,18 @@ const VolumeGraph = () => {
83101
const RADIAN = Math.PI / 180;
84102

85103
const renderLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, index, data, isInner = false }: any) => {
86-
const entry = data[index];
104+
const entry = data?.[index];
105+
106+
if (!entry) return null;
107+
87108
const radius = innerRadius + (outerRadius - innerRadius) * (isInner ? 0.5 : 1.4);
88109
const x = cx + radius * Math.cos(-midAngle * RADIAN);
89110
const y = cy + radius * Math.sin(-midAngle * RADIAN);
90111

91112
const textAnchor = isInner
92113
? (x > cx ? 'start' : 'end')
93114
: (Math.cos(midAngle * RADIAN) > 0 ? 'start' : 'end');
94-
115+
95116
return (
96117
<text
97118
x={x}
@@ -112,73 +133,95 @@ const VolumeGraph = () => {
112133
};
113134

114135
const { inbound, outbound }: any = useMemo(() => {
115-
return transformVolumeGraphData(volumeForwards);
116-
}, [volumeForwards]);
136+
return transformVolumeGraphData(filteredVolumeForwards);
137+
}, [filteredVolumeForwards]);
117138

118139
pieColors = getBarColors(inbound.length);
119140
const colorChannelMap = new Map();
120141
inbound.forEach((channel, index) => {
121142
colorChannelMap.set(channel.in_channel_scid, pieColors[inbound.length - index - 1]);
122143
});
123144

145+
const multiSelectChangeHandler = useCallback((selectedOptions: string[], mode: string) => {
146+
setTimeout(() => {
147+
setSelectedScids(selectedOptions);
148+
setFilterMode(mode);
149+
}, 0);
150+
}, []);
151+
124152
return (
125153
<div data-testid='volume-graph' className='volume-graph'>
126154
<ResponsiveContainer width='100%' key={location.key}>
127-
{animate ? (
128-
<PieChart margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>
129-
<Tooltip content={<VolumeGraphTooltip unit={uiConfigUnit} />} />
130-
<Legend
131-
content={<VolumeGraphLegend colorChannelMap={colorChannelMap} />}
132-
layout='horizontal'
133-
verticalAlign='bottom'
134-
align='center'
155+
<div className="d-flex w-100 h-100 volume-graph-container">
156+
<div className="left-column">
157+
<MultiSelectDropdown
158+
options={uniqueInChannelSCIDOptions}
159+
placeholder='Filter Inbound'
160+
onChange={multiSelectChangeHandler}
135161
/>
136-
<Pie
137-
data={inbound}
138-
cx='50%'
139-
cy='50%'
140-
labelLine={false}
141-
label={(props) => renderLabel({ ...props, data: inbound, isInner: true })}
142-
outerRadius='76%'
143-
dataKey='fee_msat'
144-
isAnimationActive={true}
145-
animationBegin={0}
146-
animationDuration={1000}
147-
nameKey='in_channel_scid'
148-
>
149-
{inbound.map((entry, index) => (
150-
<Cell
151-
key={`cell-${index}`}
152-
fill={colorChannelMap.get(entry.in_channel_scid)}
153-
stroke='#333'
154-
strokeWidth={1}
155-
/>
156-
))}
157-
</Pie>
158-
<Pie
159-
data={outbound}
160-
dataKey='fee_msat'
161-
cx='50%'
162-
cy='50%'
163-
innerRadius='80%'
164-
outerRadius='98%'
165-
label={(props) => (outbound[props.index].show_label) ? renderLabel({ ...props, data: outbound, isInner: false }) : null}
166-
labelLine={outbound.length <= TOTAL_LABELS}
167-
isAnimationActive={true}
168-
animationBegin={200}
169-
animationDuration={1000}
170-
>
171-
{outbound.map((entry, index) => (
172-
<Cell
173-
key={`outbound-cell-${index}`}
174-
fill={colorChannelMap.get(entry.in_channel_scid)}
175-
stroke='#333'
176-
strokeWidth={1}
162+
</div>
163+
<div className="right-column p-2">
164+
{animate ? (
165+
<PieChart margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>
166+
<Tooltip content={<VolumeGraphTooltip unit={uiConfigUnit} />} />
167+
<Legend
168+
content={<VolumeGraphLegend colorChannelMap={colorChannelMap} />}
169+
layout='horizontal'
170+
verticalAlign='bottom'
171+
align='center'
177172
/>
178-
))}
179-
</Pie>
180-
</PieChart>
181-
) : (<></>)}
173+
<Pie
174+
data={inbound}
175+
cx='50%'
176+
cy='50%'
177+
labelLine={false}
178+
label={(props) => renderLabel({ ...props, data: inbound, isInner: true })}
179+
outerRadius='76%'
180+
dataKey='fee_msat'
181+
isAnimationActive={true}
182+
animationBegin={0}
183+
animationDuration={1000}
184+
nameKey='in_channel_scid'
185+
>
186+
{inbound.map((entry, index) => (
187+
<Cell
188+
key={`cell-${index}`}
189+
fill={colorChannelMap.get(entry.in_channel_scid)}
190+
stroke='#333'
191+
strokeWidth={1}
192+
/>
193+
))}
194+
</Pie>
195+
<Pie
196+
data={outbound}
197+
dataKey='fee_msat'
198+
cx='50%'
199+
cy='50%'
200+
innerRadius='80%'
201+
outerRadius='98%'
202+
label={(props) => {
203+
const entry = outbound?.[props.index];
204+
if (!entry) return null;
205+
return entry.show_label ? renderLabel({ ...props, data: outbound, isInner: false }) : null;
206+
}}
207+
labelLine={outbound.length <= TOTAL_LABELS}
208+
isAnimationActive={true}
209+
animationBegin={200}
210+
animationDuration={1000}
211+
>
212+
{outbound.map((entry, index) => (
213+
<Cell
214+
key={`outbound-cell-${index}`}
215+
fill={colorChannelMap.get(entry.in_channel_scid)}
216+
stroke='#333'
217+
strokeWidth={1}
218+
/>
219+
))}
220+
</Pie>
221+
</PieChart>
222+
) : null}
223+
</div>
224+
</div>
182225
</ResponsiveContainer>
183226
</div>
184227
);

apps/frontend/src/components/bookkeeper/Volume/VolumeRoot.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { useNavigate } from 'react-router-dom';
66

77
const VolumeRoot = () => {
88
const navigate = useNavigate();
9-
109
return (
1110
<div data-testid='volume-container' className='volume-container'>
1211
<Card className='h-100 p-3 pb-4'>

0 commit comments

Comments
 (0)