Skip to content

Commit 4d2b599

Browse files
committed
Fetch data for bookkeeper landing
Add info to satsflow tooltip
1 parent 41bde55 commit 4d2b599

File tree

9 files changed

+162
-65
lines changed

9 files changed

+162
-65
lines changed

apps/frontend/src/components/bookkeeper/BalanceSheet/BalanceSheetRoot.tsx

+1-11
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,7 @@ const BalanceSheetRoot = () => {
3434

3535
const fetchBalanceSheetData = useCallback(
3636
async (timeGranularity: TimeGranularity, hideZeroActivityPeriods: Boolean, startDate?: Date, endDate?: Date) => {
37-
let startTimestamp = 1;
38-
let endTimestamp = Math.floor(new Date().getTime());
39-
40-
if (startDate != null) {
41-
startTimestamp = Math.floor(startDate.getTime() / 1000);
42-
}
43-
if (endDate != null) {
44-
endTimestamp = Math.floor(endDate.getTime() / 1000);
45-
}
46-
47-
getBalanceSheet(timeGranularity, hideZeroActivityPeriods, startTimestamp, endTimestamp)
37+
getBalanceSheet(timeGranularity, hideZeroActivityPeriods, startDate, endDate)
4838
.then((response: BalanceSheet) => {
4939
setBalanceSheetData(response);
5040
})

apps/frontend/src/components/bookkeeper/BkprRoot/BkprRoot.tsx

+53-18
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,46 @@ import { SatsFlowSVG } from '../../../svgs/SatsFlow';
1010
import { VolumeChartSVG } from '../../../svgs/VolumeChart';
1111
import SatsFlowInfo from './SatsFlowInfo';
1212
import VolumeInfo from './VolumeInfo';
13+
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
14+
import useHttp from '../../../hooks/use-http';
15+
import { AppContext } from '../../../store/AppContext';
16+
import { BookkeeperLandingData } from '../../../types/lightning-bookkeeper-landing.type';
17+
import { BALANCE_FORMAT } from '../../../utilities/constants';
18+
import { format } from 'd3';
1319

14-
function Bookkeeper() {
20+
const Bookkeeper = () => {
21+
const appCtx = useContext(AppContext);
1522
const navigate = useNavigate();
23+
const [bookkeeperLandingData, setBookkeeperLandingData] = useState<BookkeeperLandingData>();
24+
const { getBookkeeperLanding } = useHttp();
25+
const formatBalance = useMemo(() => format(BALANCE_FORMAT), []);
26+
27+
const fetchBookkeeperLandingData = useCallback(
28+
async () => {
29+
getBookkeeperLanding()
30+
.then((response: BookkeeperLandingData) => {
31+
setBookkeeperLandingData(response);
32+
})
33+
.catch(err => {
34+
console.error("fetchBookkeeperLandingData error" + err);
35+
});
36+
},
37+
// eslint-disable-next-line react-hooks/exhaustive-deps
38+
[],
39+
);
40+
41+
useEffect(() => {
42+
if (appCtx.authStatus.isAuthenticated) {
43+
fetchBookkeeperLandingData();
44+
}
45+
}, [appCtx.authStatus.isAuthenticated, fetchBookkeeperLandingData]);
46+
1647
return (
1748
<div data-testid="bookkeeper-container">
1849
<div className="fs-4 p-0 ps-3 fw-bold text-dark">Bookkeeper Dashboard</div>
1950
<Container fluid className="">
2051
<Row className="">
21-
<Col md={5} xs={12} className="">
52+
<Col lg={5} md={12} xs={12} className="">
2253
<Card className="mt-4 pt-4 px-4 inner-box-shadow">
2354
<div className="d-flex align-items-start">
2455
<BalanceSheetSVG className="me-3" />
@@ -31,15 +62,15 @@ function Bookkeeper() {
3162
<Card.Body className="px-0 pt-0">
3263
Track channel and wallet balances over time.
3364
<div className="mt-5 d-flex flex-column align-items-start">
34-
<span className="fs-1 fw-bold text-primary">4</span>
65+
<span className="fs-1 fw-bold text-primary">{bookkeeperLandingData?.balanceSheetSummary.numberOfChannels}</span>
3566
<span className="fs-6 text-dark">Total Number of Channels</span>
3667
</div>
3768
<div className="mt-3 d-flex flex-column align-items-start">
38-
<span className="fs-1 fw-bold text-primary">4,000,000</span>
69+
<span className="fs-1 fw-bold text-primary">{formatBalance(bookkeeperLandingData?.balanceSheetSummary.balanceInChannels || 0)}</span>
3970
<span className="fs-6 text-dark">Total Balance in Channels</span>
4071
</div>
4172
<div className="mt-3 d-flex flex-column align-items-start">
42-
<span className="fs-1 fw-bold text-primary">4</span>
73+
<span className="fs-1 fw-bold text-primary">{formatBalance(bookkeeperLandingData?.balanceSheetSummary.balanceInWallet || 0)}</span>
4374
<span className="fs-6 text-dark">Total Balance in Wallet</span>
4475
</div>
4576
</Card.Body>
@@ -58,7 +89,7 @@ function Bookkeeper() {
5889
</div>
5990
</Card>
6091
</Col>
61-
<Col md={6} xs={12} className="ms-md-4 ms-xs-0 px-4 mt-4 d-flex flex-column">
92+
<Col lg={6} md={12} xs={12} className="ms-lg-4 ms-xs-0 px-4 mt-4 d-flex flex-column">
6293
<Row>
6394
<Card className="pt-4 px-4 inner-box-shadow">
6495
<div className="d-flex align-items-start">
@@ -72,11 +103,19 @@ function Bookkeeper() {
72103
<Card.Body className="px-0 pt-0">
73104
Track inflows and outflow events over time.
74105
<Row className="g-3 flex-wrap">
75-
<Col xs={12} md={12} lg={12} xl={6} className="d-flex flex-column align-items-start">
76-
<SatsFlowInfo label={'Inflow this month'} value={-90000000000} />
106+
<Col
107+
lg={12}
108+
xl={6}
109+
className="d-flex flex-column align-items-start"
110+
>
111+
<SatsFlowInfo label={'Inflow this month'} value={bookkeeperLandingData?.satsFlowSummary.inflows || 0} />
77112
</Col>
78-
<Col xs={12} md={12} lg={12} xl={6} className="d-flex flex-column align-items-start">
79-
<SatsFlowInfo label={'Outflow this month'} value={4000000000} />
113+
<Col
114+
lg={12}
115+
xl={6}
116+
className="d-flex flex-column align-items-start"
117+
>
118+
<SatsFlowInfo label={'Outflow this month'} value={-(bookkeeperLandingData?.satsFlowSummary.outflows || 0)} />
80119
</Col>
81120
</Row>
82121
</Card.Body>
@@ -108,20 +147,16 @@ function Bookkeeper() {
108147
<Card.Body className="px-0 pt-0">
109148
Track channel routing performance.
110149
<VolumeInfo
111-
bestRoutingChannel={
112-
'41b2aeea3791fcd38d9d1e0183075868808fad06092b3224e91b8b16a4ec3a6b'
113-
}
114-
worstRoutingChannel={
115-
'41b2aeea3791fcd38d9d1e0183075868808fad06092b3224e91b8b16a4ec3a6b'
116-
}
150+
bestRoutingChannel={bookkeeperLandingData?.volumeSummary.mostTrafficChannel}
151+
worstRoutingChannel={bookkeeperLandingData?.volumeSummary.leastTrafficChannel}
117152
/>
118153
</Card.Body>
119154
<Card.Footer className="mt-3 mb-3 d-flex justify-content-end">
120155
<button
121156
tabIndex={1}
122157
type="submit"
123158
className="btn-rounded bg-primary"
124-
onClick={() => navigate('/bookkeeper/satsflow')}
159+
onClick={() => navigate('/bookkeeper/volume')}
125160
>
126161
View More
127162
<ActionSVG className="ms-3" />
@@ -136,6 +171,6 @@ function Bookkeeper() {
136171
</Container>
137172
</div>
138173
);
139-
}
174+
};
140175

141176
export default Bookkeeper;

apps/frontend/src/components/bookkeeper/BkprRoot/VolumeInfo.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import React from 'react';
33
import './VolumeInfo.scss';
44

55
interface VolumeInfoProps {
6-
bestRoutingChannel: string;
7-
worstRoutingChannel: string;
6+
bestRoutingChannel?: string;
7+
worstRoutingChannel?: string;
88
}
99

1010
const VolumeInfo: React.FC<VolumeInfoProps> = ({ bestRoutingChannel, worstRoutingChannel }) => {

apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowGraph/SatsFlowGraph.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import * as d3 from "d3";
22
import { format } from 'd3-format';
3-
import { useRef, useEffect } from "react";
3+
import { useRef, useEffect, useMemo } from "react";
44
import './SatsFlowGraph.scss';
55
import { SatsFlow, SatsFlowPeriod, TagGroup } from "../../../../types/lightning-satsflow.type";
6+
import { BALANCE_FORMAT } from '../../../../utilities/constants';
67

78
function SatsFlowGraph({ satsFlowData, width }: { satsFlowData: SatsFlow, width: number }) {
89
const d3Container = useRef(null);
910
const tooltipRef = useRef<HTMLDivElement | null>(null);
11+
const formatSats = useMemo(() => format(BALANCE_FORMAT), []);
1012

1113
useEffect(() => {
1214
if (d3Container.current && satsFlowData.periods.length > 0) {
@@ -17,7 +19,6 @@ function SatsFlowGraph({ satsFlowData, width }: { satsFlowData: SatsFlow, width:
1719
const margin = { top: 10, right: 30, bottom: 30, left: 100 };
1820
const innerWidth = outerWidth - margin.left - margin.right;
1921
const innerHeight = outerHeight - margin.top - margin.bottom;
20-
const formatSats = format(',.3f');
2122

2223
const { maxInflowSat, maxOutflowSat } = findMaxInflowAndOutflow(satsFlowData);
2324

@@ -114,7 +115,11 @@ function SatsFlowGraph({ satsFlowData, width }: { satsFlowData: SatsFlow, width:
114115
Net Inflow: ${formatSats(tagGroup.netInflowSat)}
115116
Credits: ${formatSats(tagGroup.creditSat)}
116117
Debits: ${formatSats(tagGroup.debitSat)}
117-
Volume: ${formatSats(tagGroup.volumeSat)}`
118+
Volume: ${formatSats(tagGroup.volumeSat)}
119+
Period Inflow: ${formatSats(period.inflowSat)}
120+
Period Outflow: ${formatSats(period.outflowSat)}
121+
Period Net Inflow: ${formatSats(period.netInflowSat)}
122+
Period Volume: ${formatSats(period.totalVolumeSat)}`
118123
);
119124
})
120125
.on("mousemove", function (event) {

apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowRoot.tsx

+1-11
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,7 @@ const SatsFlowRoot = (props) => {
2727
};
2828

2929
const fetchSatsFlowData = useCallback(async (timeGranularity: TimeGranularity, hideZeroActivityPeriods: boolean, startDate?: Date, endDate?: Date) => {
30-
let startTimestamp = 1;
31-
let endTimestamp = Math.floor(new Date().getTime());
32-
33-
if (startDate != null) {
34-
startTimestamp = Math.floor(startDate.getTime() / 1000);
35-
}
36-
if (endDate != null) {
37-
endTimestamp = Math.floor(endDate.getTime() / 1000);
38-
}
39-
40-
getSatsFlow(timeGranularity, hideZeroActivityPeriods, startTimestamp, endTimestamp)
30+
getSatsFlow(timeGranularity, hideZeroActivityPeriods, startDate, endDate)
4131
.then((response: SatsFlow) => {
4232
setSatsFlowData(response);
4333
})

apps/frontend/src/hooks/use-http.ts

+75-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { faDollarSign } from '@fortawesome/free-solid-svg-icons';
88
import { isCompatibleVersion } from '../utilities/data-formatters';
99
import { BalanceSheetSQL, SatsFlowSQL, VolumeSQL } from '../sql/bookkeeper-sql';
1010
import { transformToBalanceSheet, transformToSatsFlow, transformToVolumeData } from '../sql/bookkeeper-transform';
11+
import { BookkeeperLandingData } from '../types/lightning-bookkeeper-landing.type';
1112

1213
let intervalID;
1314
let localAuthStatus: any = null;
@@ -163,26 +164,93 @@ const useHttp = () => {
163164
const btcDeposit = () => {
164165
return sendRequest(false, 'post', '/cln/call', { 'method': 'newaddr', 'params': { 'addresstype': 'bech32' } });
165166
};
167+
168+
const getBookkeeperLanding = async (): Promise<BookkeeperLandingData> => {
169+
const balanceSheetData = await getBalanceSheet(TimeGranularity.MONTHLY, true, undefined, new Date());
170+
const satsFlowData = await getSatsFlow(TimeGranularity.MONTHLY, true, undefined, new Date());
171+
const volumeData = await getVolumeData();
172+
173+
const latestBalanceSheetPeriod = balanceSheetData.periods[balanceSheetData.periods.length - 1];
174+
const balanceInWallet = latestBalanceSheetPeriod.accounts.filter(() => "wallet")[0].balance;
175+
const balanceInChannels = latestBalanceSheetPeriod.accounts
176+
.filter(account => account.account !== "wallet")
177+
.reduce((sum, account) => sum + account.balance, 0);
178+
const numberOfChannels = latestBalanceSheetPeriod.accounts
179+
.filter(account => account.account !== "wallet")
180+
.reduce((sum) => sum + 1, 0);
181+
182+
const latestSatsFlowPeriod = satsFlowData.periods[satsFlowData.periods.length - 1];
183+
const inflowsThisMonth = latestSatsFlowPeriod.inflowSat;
184+
const outflowsThisMonth = latestSatsFlowPeriod.outflowSat;
185+
186+
187+
return {
188+
balanceSheetSummary: {
189+
balanceInWallet: balanceInWallet,
190+
balanceInChannels: balanceInChannels,
191+
numberOfChannels: numberOfChannels,
192+
},
193+
satsFlowSummary: {
194+
inflows: inflowsThisMonth,
195+
outflows: outflowsThisMonth,
196+
},
197+
volumeSummary: {
198+
mostTrafficChannel: "best",
199+
leastTrafficChannel: "worst",
200+
}
201+
};
202+
};
166203

167204
/**
168205
* Gets Balance Sheet data.
169206
* @param timeGranularity - Group data by this time granularity.
170-
* @param startTimestamp - If specified, the starting range for the data.
171-
* @param endTimestamp - The ending range for the data.
207+
* @param hideZeroActivityPeriods - Hide bars where balance did not change.
208+
* @param startTimestamp - The starting range for the data if specified, else uses beginning.
209+
* @param endTimestamp - The ending range for the data if specified, else uses now.
172210
* @returns Returns balance data grouped in periods of the specified time granularity.
173211
*/
174-
const getBalanceSheet = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: Boolean, startTimestamp: number, endTimestamp: number) => {
212+
const getBalanceSheet = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: Boolean, startDate?: Date, endDate?: Date) => {
213+
let startTimestamp = 1;
214+
let endTimestamp = Math.floor(new Date().getTime());
215+
216+
if (startDate != null) {
217+
startTimestamp = Math.floor(startDate.getTime() / 1000);
218+
}
219+
if (endDate != null) {
220+
endTimestamp = Math.floor(endDate.getTime() / 1000);
221+
}
175222
return sendRequest(false, 'post', '/cln/call', { 'method': 'sql', 'params': [BalanceSheetSQL] })
176223
.then((response) => transformToBalanceSheet(response.data, timeGranularity, hideZeroActivityPeriods, startTimestamp, endTimestamp));
177224
};
178225

179-
const getSatsFlow = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: boolean, startTimestamp: number, endTimestamp: number) => {
180-
return sendRequest(false, 'post', 'cln/call', { 'method': 'sql', 'params': [SatsFlowSQL(startTimestamp, endTimestamp)] })
226+
/**
227+
* Gets Sats Flow data.
228+
* @param timeGranularity - Group data by this time granularity.
229+
* @param hideZeroActivityPeriods - Hide bars where balance did not change.
230+
* @param startTimestamp - The starting range for the data if specified, else uses beginning.
231+
* @param endTimestamp - The ending range for the data, else uses now.
232+
* @returns Returns sats flow data grouped in periods of the specified time granularity.
233+
*/
234+
const getSatsFlow = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: boolean, startDate?: Date, endDate?: Date) => {
235+
let startTimestamp = 1;
236+
let endTimestamp = Math.floor(new Date().getTime());
237+
238+
if (startDate != null) {
239+
startTimestamp = Math.floor(startDate.getTime() / 1000);
240+
}
241+
if (endDate != null) {
242+
endTimestamp = Math.floor(endDate.getTime() / 1000);
243+
}
244+
return sendRequest(false, 'post', '/cln/call', { 'method': 'sql', 'params': [SatsFlowSQL(startTimestamp, endTimestamp)] })
181245
.then((response) => transformToSatsFlow(response.data, timeGranularity, hideZeroActivityPeriods));
182246
};
183247

248+
/**
249+
* Gets Volume Chart data.
250+
* @returns Returns balance data grouped in periods of the specified time granularity.
251+
*/
184252
const getVolumeData = () => {
185-
return sendRequest(false, 'post', 'cln/call', { 'method': 'sql', 'params': [VolumeSQL] })
253+
return sendRequest(false, 'post', '/cln/call', { 'method': 'sql', 'params': [VolumeSQL] })
186254
.then((response) => transformToVolumeData(response.data));
187255
};
188256

@@ -330,6 +398,7 @@ const useHttp = () => {
330398
decodeInvoice,
331399
fetchInvoice,
332400
createInvoiceRune,
401+
getBookkeeperLanding,
333402
getBalanceSheet,
334403
getSatsFlow,
335404
getVolumeData,

apps/frontend/src/sql/bookkeeper-transform.ts

-14
Original file line numberDiff line numberDiff line change
@@ -457,17 +457,3 @@ function getTag(event: SatsFlowEvent): string {
457457
return event.tag;
458458
}
459459
}
460-
461-
/**
462-
* Compare BalanceSheetRow[] objects.
463-
*
464-
* @param rowA - The first set of rows to compare.
465-
* @param rowB - The second set of rows to compare.
466-
* @returns Returns true if both lists of rows are equal.
467-
*/
468-
function areBalanceSheetRowsEqual(rowsA: BalanceSheetRow[], rowsB: BalanceSheetRow[]): boolean {
469-
if (!rowsA || !rowsB || rowsA.length !== rowsB.length) {
470-
return false;
471-
}
472-
return rowsA.every((row, index) => JSON.stringify(row) === JSON.stringify(rowsB[index]));
473-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export type BookkeeperLandingData = {
2+
balanceSheetSummary: BalanceSheetSummary;
3+
satsFlowSummary: SatsFlowSummary;
4+
volumeSummary: VolumeSummary;
5+
};
6+
7+
export type BalanceSheetSummary = {
8+
balanceInWallet: number;
9+
balanceInChannels: number;
10+
numberOfChannels: number;
11+
};
12+
13+
export type SatsFlowSummary = {
14+
inflows: number;
15+
outflows: number;
16+
};
17+
18+
export type VolumeSummary = {
19+
mostTrafficChannel: string;
20+
leastTrafficChannel: string;
21+
}
22+

entrypoint.sh

100644100755
File mode changed.

0 commit comments

Comments
 (0)