From 4d2b5999cc7abbfc58f43737e1f8a84c7ab8ef15 Mon Sep 17 00:00:00 2001 From: evansmj Date: Mon, 23 Dec 2024 17:34:07 -0500 Subject: [PATCH] Fetch data for bookkeeper landing Add info to satsflow tooltip --- .../BalanceSheet/BalanceSheetRoot.tsx | 12 +-- .../bookkeeper/BkprRoot/BkprRoot.tsx | 71 +++++++++++----- .../bookkeeper/BkprRoot/VolumeInfo.tsx | 4 +- .../SatsFlow/SatsFlowGraph/SatsFlowGraph.tsx | 11 ++- .../bookkeeper/SatsFlow/SatsFlowRoot.tsx | 12 +-- apps/frontend/src/hooks/use-http.ts | 81 +++++++++++++++++-- apps/frontend/src/sql/bookkeeper-transform.ts | 14 ---- .../lightning-bookkeeper-landing.type.ts | 22 +++++ entrypoint.sh | 0 9 files changed, 162 insertions(+), 65 deletions(-) create mode 100644 apps/frontend/src/types/lightning-bookkeeper-landing.type.ts mode change 100644 => 100755 entrypoint.sh diff --git a/apps/frontend/src/components/bookkeeper/BalanceSheet/BalanceSheetRoot.tsx b/apps/frontend/src/components/bookkeeper/BalanceSheet/BalanceSheetRoot.tsx index c27b86a..9520031 100644 --- a/apps/frontend/src/components/bookkeeper/BalanceSheet/BalanceSheetRoot.tsx +++ b/apps/frontend/src/components/bookkeeper/BalanceSheet/BalanceSheetRoot.tsx @@ -34,17 +34,7 @@ const BalanceSheetRoot = () => { const fetchBalanceSheetData = useCallback( async (timeGranularity: TimeGranularity, hideZeroActivityPeriods: Boolean, startDate?: Date, endDate?: Date) => { - let startTimestamp = 1; - let endTimestamp = Math.floor(new Date().getTime()); - - if (startDate != null) { - startTimestamp = Math.floor(startDate.getTime() / 1000); - } - if (endDate != null) { - endTimestamp = Math.floor(endDate.getTime() / 1000); - } - - getBalanceSheet(timeGranularity, hideZeroActivityPeriods, startTimestamp, endTimestamp) + getBalanceSheet(timeGranularity, hideZeroActivityPeriods, startDate, endDate) .then((response: BalanceSheet) => { setBalanceSheetData(response); }) diff --git a/apps/frontend/src/components/bookkeeper/BkprRoot/BkprRoot.tsx b/apps/frontend/src/components/bookkeeper/BkprRoot/BkprRoot.tsx index a934e1e..f76f6a9 100644 --- a/apps/frontend/src/components/bookkeeper/BkprRoot/BkprRoot.tsx +++ b/apps/frontend/src/components/bookkeeper/BkprRoot/BkprRoot.tsx @@ -10,15 +10,46 @@ import { SatsFlowSVG } from '../../../svgs/SatsFlow'; import { VolumeChartSVG } from '../../../svgs/VolumeChart'; import SatsFlowInfo from './SatsFlowInfo'; import VolumeInfo from './VolumeInfo'; +import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import useHttp from '../../../hooks/use-http'; +import { AppContext } from '../../../store/AppContext'; +import { BookkeeperLandingData } from '../../../types/lightning-bookkeeper-landing.type'; +import { BALANCE_FORMAT } from '../../../utilities/constants'; +import { format } from 'd3'; -function Bookkeeper() { +const Bookkeeper = () => { + const appCtx = useContext(AppContext); const navigate = useNavigate(); + const [bookkeeperLandingData, setBookkeeperLandingData] = useState(); + const { getBookkeeperLanding } = useHttp(); + const formatBalance = useMemo(() => format(BALANCE_FORMAT), []); + + const fetchBookkeeperLandingData = useCallback( + async () => { + getBookkeeperLanding() + .then((response: BookkeeperLandingData) => { + setBookkeeperLandingData(response); + }) + .catch(err => { + console.error("fetchBookkeeperLandingData error" + err); + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + useEffect(() => { + if (appCtx.authStatus.isAuthenticated) { + fetchBookkeeperLandingData(); + } + }, [appCtx.authStatus.isAuthenticated, fetchBookkeeperLandingData]); + return (
Bookkeeper Dashboard
- +
@@ -31,15 +62,15 @@ function Bookkeeper() { Track channel and wallet balances over time.
- 4 + {bookkeeperLandingData?.balanceSheetSummary.numberOfChannels} Total Number of Channels
- 4,000,000 + {formatBalance(bookkeeperLandingData?.balanceSheetSummary.balanceInChannels || 0)} Total Balance in Channels
- 4 + {formatBalance(bookkeeperLandingData?.balanceSheetSummary.balanceInWallet || 0)} Total Balance in Wallet
@@ -58,7 +89,7 @@ function Bookkeeper() {
- +
@@ -72,11 +103,19 @@ function Bookkeeper() { Track inflows and outflow events over time. - - + + - - + + @@ -108,12 +147,8 @@ function Bookkeeper() { Track channel routing performance. @@ -121,7 +156,7 @@ function Bookkeeper() { tabIndex={1} type="submit" className="btn-rounded bg-primary" - onClick={() => navigate('/bookkeeper/satsflow')} + onClick={() => navigate('/bookkeeper/volume')} > View More @@ -136,6 +171,6 @@ function Bookkeeper() {
); -} +}; export default Bookkeeper; diff --git a/apps/frontend/src/components/bookkeeper/BkprRoot/VolumeInfo.tsx b/apps/frontend/src/components/bookkeeper/BkprRoot/VolumeInfo.tsx index b6306b5..57c506a 100644 --- a/apps/frontend/src/components/bookkeeper/BkprRoot/VolumeInfo.tsx +++ b/apps/frontend/src/components/bookkeeper/BkprRoot/VolumeInfo.tsx @@ -3,8 +3,8 @@ import React from 'react'; import './VolumeInfo.scss'; interface VolumeInfoProps { - bestRoutingChannel: string; - worstRoutingChannel: string; + bestRoutingChannel?: string; + worstRoutingChannel?: string; } const VolumeInfo: React.FC = ({ bestRoutingChannel, worstRoutingChannel }) => { diff --git a/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowGraph/SatsFlowGraph.tsx b/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowGraph/SatsFlowGraph.tsx index 94125e5..956b47e 100644 --- a/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowGraph/SatsFlowGraph.tsx +++ b/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowGraph/SatsFlowGraph.tsx @@ -1,12 +1,14 @@ import * as d3 from "d3"; import { format } from 'd3-format'; -import { useRef, useEffect } from "react"; +import { useRef, useEffect, useMemo } from "react"; import './SatsFlowGraph.scss'; import { SatsFlow, SatsFlowPeriod, TagGroup } from "../../../../types/lightning-satsflow.type"; +import { BALANCE_FORMAT } from '../../../../utilities/constants'; function SatsFlowGraph({ satsFlowData, width }: { satsFlowData: SatsFlow, width: number }) { const d3Container = useRef(null); const tooltipRef = useRef(null); + const formatSats = useMemo(() => format(BALANCE_FORMAT), []); useEffect(() => { if (d3Container.current && satsFlowData.periods.length > 0) { @@ -17,7 +19,6 @@ function SatsFlowGraph({ satsFlowData, width }: { satsFlowData: SatsFlow, width: const margin = { top: 10, right: 30, bottom: 30, left: 100 }; const innerWidth = outerWidth - margin.left - margin.right; const innerHeight = outerHeight - margin.top - margin.bottom; - const formatSats = format(',.3f'); const { maxInflowSat, maxOutflowSat } = findMaxInflowAndOutflow(satsFlowData); @@ -114,7 +115,11 @@ function SatsFlowGraph({ satsFlowData, width }: { satsFlowData: SatsFlow, width: Net Inflow: ${formatSats(tagGroup.netInflowSat)} Credits: ${formatSats(tagGroup.creditSat)} Debits: ${formatSats(tagGroup.debitSat)} - Volume: ${formatSats(tagGroup.volumeSat)}` + Volume: ${formatSats(tagGroup.volumeSat)} + Period Inflow: ${formatSats(period.inflowSat)} + Period Outflow: ${formatSats(period.outflowSat)} + Period Net Inflow: ${formatSats(period.netInflowSat)} + Period Volume: ${formatSats(period.totalVolumeSat)}` ); }) .on("mousemove", function (event) { diff --git a/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowRoot.tsx b/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowRoot.tsx index c03c771..1c4d70f 100644 --- a/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowRoot.tsx +++ b/apps/frontend/src/components/bookkeeper/SatsFlow/SatsFlowRoot.tsx @@ -27,17 +27,7 @@ const SatsFlowRoot = (props) => { }; const fetchSatsFlowData = useCallback(async (timeGranularity: TimeGranularity, hideZeroActivityPeriods: boolean, startDate?: Date, endDate?: Date) => { - let startTimestamp = 1; - let endTimestamp = Math.floor(new Date().getTime()); - - if (startDate != null) { - startTimestamp = Math.floor(startDate.getTime() / 1000); - } - if (endDate != null) { - endTimestamp = Math.floor(endDate.getTime() / 1000); - } - - getSatsFlow(timeGranularity, hideZeroActivityPeriods, startTimestamp, endTimestamp) + getSatsFlow(timeGranularity, hideZeroActivityPeriods, startDate, endDate) .then((response: SatsFlow) => { setSatsFlowData(response); }) diff --git a/apps/frontend/src/hooks/use-http.ts b/apps/frontend/src/hooks/use-http.ts index 06dd786..abc99bb 100755 --- a/apps/frontend/src/hooks/use-http.ts +++ b/apps/frontend/src/hooks/use-http.ts @@ -8,6 +8,7 @@ import { faDollarSign } from '@fortawesome/free-solid-svg-icons'; import { isCompatibleVersion } from '../utilities/data-formatters'; import { BalanceSheetSQL, SatsFlowSQL, VolumeSQL } from '../sql/bookkeeper-sql'; import { transformToBalanceSheet, transformToSatsFlow, transformToVolumeData } from '../sql/bookkeeper-transform'; +import { BookkeeperLandingData } from '../types/lightning-bookkeeper-landing.type'; let intervalID; let localAuthStatus: any = null; @@ -163,26 +164,93 @@ const useHttp = () => { const btcDeposit = () => { return sendRequest(false, 'post', '/cln/call', { 'method': 'newaddr', 'params': { 'addresstype': 'bech32' } }); }; + + const getBookkeeperLanding = async (): Promise => { + const balanceSheetData = await getBalanceSheet(TimeGranularity.MONTHLY, true, undefined, new Date()); + const satsFlowData = await getSatsFlow(TimeGranularity.MONTHLY, true, undefined, new Date()); + const volumeData = await getVolumeData(); + + const latestBalanceSheetPeriod = balanceSheetData.periods[balanceSheetData.periods.length - 1]; + const balanceInWallet = latestBalanceSheetPeriod.accounts.filter(() => "wallet")[0].balance; + const balanceInChannels = latestBalanceSheetPeriod.accounts + .filter(account => account.account !== "wallet") + .reduce((sum, account) => sum + account.balance, 0); + const numberOfChannels = latestBalanceSheetPeriod.accounts + .filter(account => account.account !== "wallet") + .reduce((sum) => sum + 1, 0); + + const latestSatsFlowPeriod = satsFlowData.periods[satsFlowData.periods.length - 1]; + const inflowsThisMonth = latestSatsFlowPeriod.inflowSat; + const outflowsThisMonth = latestSatsFlowPeriod.outflowSat; + + + return { + balanceSheetSummary: { + balanceInWallet: balanceInWallet, + balanceInChannels: balanceInChannels, + numberOfChannels: numberOfChannels, + }, + satsFlowSummary: { + inflows: inflowsThisMonth, + outflows: outflowsThisMonth, + }, + volumeSummary: { + mostTrafficChannel: "best", + leastTrafficChannel: "worst", + } + }; + }; /** * Gets Balance Sheet data. * @param timeGranularity - Group data by this time granularity. - * @param startTimestamp - If specified, the starting range for the data. - * @param endTimestamp - The ending range for the data. + * @param hideZeroActivityPeriods - Hide bars where balance did not change. + * @param startTimestamp - The starting range for the data if specified, else uses beginning. + * @param endTimestamp - The ending range for the data if specified, else uses now. * @returns Returns balance data grouped in periods of the specified time granularity. */ - const getBalanceSheet = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: Boolean, startTimestamp: number, endTimestamp: number) => { + const getBalanceSheet = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: Boolean, startDate?: Date, endDate?: Date) => { + let startTimestamp = 1; + let endTimestamp = Math.floor(new Date().getTime()); + + if (startDate != null) { + startTimestamp = Math.floor(startDate.getTime() / 1000); + } + if (endDate != null) { + endTimestamp = Math.floor(endDate.getTime() / 1000); + } return sendRequest(false, 'post', '/cln/call', { 'method': 'sql', 'params': [BalanceSheetSQL] }) .then((response) => transformToBalanceSheet(response.data, timeGranularity, hideZeroActivityPeriods, startTimestamp, endTimestamp)); }; - const getSatsFlow = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: boolean, startTimestamp: number, endTimestamp: number) => { - return sendRequest(false, 'post', 'cln/call', { 'method': 'sql', 'params': [SatsFlowSQL(startTimestamp, endTimestamp)] }) + /** + * Gets Sats Flow data. + * @param timeGranularity - Group data by this time granularity. + * @param hideZeroActivityPeriods - Hide bars where balance did not change. + * @param startTimestamp - The starting range for the data if specified, else uses beginning. + * @param endTimestamp - The ending range for the data, else uses now. + * @returns Returns sats flow data grouped in periods of the specified time granularity. + */ + const getSatsFlow = (timeGranularity: TimeGranularity, hideZeroActivityPeriods: boolean, startDate?: Date, endDate?: Date) => { + let startTimestamp = 1; + let endTimestamp = Math.floor(new Date().getTime()); + + if (startDate != null) { + startTimestamp = Math.floor(startDate.getTime() / 1000); + } + if (endDate != null) { + endTimestamp = Math.floor(endDate.getTime() / 1000); + } + return sendRequest(false, 'post', '/cln/call', { 'method': 'sql', 'params': [SatsFlowSQL(startTimestamp, endTimestamp)] }) .then((response) => transformToSatsFlow(response.data, timeGranularity, hideZeroActivityPeriods)); }; + /** + * Gets Volume Chart data. + * @returns Returns balance data grouped in periods of the specified time granularity. + */ const getVolumeData = () => { - return sendRequest(false, 'post', 'cln/call', { 'method': 'sql', 'params': [VolumeSQL] }) + return sendRequest(false, 'post', '/cln/call', { 'method': 'sql', 'params': [VolumeSQL] }) .then((response) => transformToVolumeData(response.data)); }; @@ -330,6 +398,7 @@ const useHttp = () => { decodeInvoice, fetchInvoice, createInvoiceRune, + getBookkeeperLanding, getBalanceSheet, getSatsFlow, getVolumeData, diff --git a/apps/frontend/src/sql/bookkeeper-transform.ts b/apps/frontend/src/sql/bookkeeper-transform.ts index 6ec54bd..302fc97 100644 --- a/apps/frontend/src/sql/bookkeeper-transform.ts +++ b/apps/frontend/src/sql/bookkeeper-transform.ts @@ -457,17 +457,3 @@ function getTag(event: SatsFlowEvent): string { return event.tag; } } - -/** - * Compare BalanceSheetRow[] objects. - * - * @param rowA - The first set of rows to compare. - * @param rowB - The second set of rows to compare. - * @returns Returns true if both lists of rows are equal. - */ -function areBalanceSheetRowsEqual(rowsA: BalanceSheetRow[], rowsB: BalanceSheetRow[]): boolean { - if (!rowsA || !rowsB || rowsA.length !== rowsB.length) { - return false; - } - return rowsA.every((row, index) => JSON.stringify(row) === JSON.stringify(rowsB[index])); -} diff --git a/apps/frontend/src/types/lightning-bookkeeper-landing.type.ts b/apps/frontend/src/types/lightning-bookkeeper-landing.type.ts new file mode 100644 index 0000000..4bb008e --- /dev/null +++ b/apps/frontend/src/types/lightning-bookkeeper-landing.type.ts @@ -0,0 +1,22 @@ +export type BookkeeperLandingData = { + balanceSheetSummary: BalanceSheetSummary; + satsFlowSummary: SatsFlowSummary; + volumeSummary: VolumeSummary; +}; + +export type BalanceSheetSummary = { + balanceInWallet: number; + balanceInChannels: number; + numberOfChannels: number; +}; + +export type SatsFlowSummary = { + inflows: number; + outflows: number; +}; + +export type VolumeSummary = { + mostTrafficChannel: string; + leastTrafficChannel: string; +} + diff --git a/entrypoint.sh b/entrypoint.sh old mode 100644 new mode 100755