diff --git a/pages/tools.tsx b/pages/tools.tsx
index 7696ff81..066b1bde 100644
--- a/pages/tools.tsx
+++ b/pages/tools.tsx
@@ -2,12 +2,14 @@ import React from 'react';
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
import { withApollo } from 'config/client';
import { Bakery } from 'src/views/tools/bakery/Bakery';
+import { Accounting } from 'src/views/tools/accounting/Accounting';
import { BackupsView } from '../src/views/tools/backups/Backups';
import { MessagesView } from '../src/views/tools/messages/Messages';
import { WalletVersion } from '../src/views/tools/WalletVersion';
const ToolsView = () => (
<>
+
diff --git a/server/schema/bos/resolvers.ts b/server/schema/bos/resolvers.ts
index 6e69e183..67d41bba 100644
--- a/server/schema/bos/resolvers.ts
+++ b/server/schema/bos/resolvers.ts
@@ -1,10 +1,13 @@
import { ContextType } from 'server/types/apiTypes';
import { getLnd } from 'server/helpers/helpers';
-import { rebalance } from 'balanceofsatoshis/swaps';
import { to } from 'server/helpers/async';
import { logger } from 'server/helpers/logger';
import { AuthType } from 'src/context/AccountContext';
+import { rebalance } from 'balanceofsatoshis/swaps';
+import { getAccountingReport } from 'balanceofsatoshis/balances';
+import request from '@alexbosworth/request';
+
type RebalanceType = {
auth: AuthType;
avoid?: String[];
@@ -19,21 +22,79 @@ type RebalanceType = {
target?: Number;
};
+type AccountingType = {
+ auth: AuthType;
+ category?: String;
+ currency?: String;
+ fiat?: String;
+ month?: String;
+ year?: String;
+};
+
export const bosResolvers = {
+ Query: {
+ getAccountingReport: async (
+ _: undefined,
+ params: AccountingType,
+ context: ContextType
+ ) => {
+ const { auth, ...settings } = params;
+ const lnd = getLnd(auth, context);
+
+ const response = await to(
+ getAccountingReport({
+ lnd,
+ logger,
+ request,
+ is_csv: true,
+ ...settings,
+ })
+ );
+
+ return response;
+ },
+ },
Mutation: {
bosRebalance: async (
_: undefined,
params: RebalanceType,
context: ContextType
) => {
- const { auth, ...extraparams } = params;
+ const {
+ auth,
+ avoid,
+ in_through,
+ is_avoiding_high_inbound,
+ max_fee,
+ max_fee_rate,
+ max_rebalance,
+ node,
+ out_channels,
+ out_through,
+ target,
+ } = params;
const lnd = getLnd(auth, context);
+ const filteredParams = {
+ ...(avoid.length > 0 && { avoid }),
+ ...(in_through && { in_through }),
+ ...(is_avoiding_high_inbound && { is_avoiding_high_inbound }),
+ ...(max_fee > 0 && { max_fee }),
+ ...(max_fee_rate > 0 && { max_fee_rate }),
+ ...(max_rebalance > 0 && { max_rebalance }),
+ ...(node && { node }),
+ ...(out_channels.length > 0 && { out_channels }),
+ ...(out_through && { out_through }),
+ ...(target && { target }),
+ };
+
+ logger.info('Rebalance Params: %o', filteredParams);
+
const response = await to(
rebalance({
lnd,
logger,
- ...extraparams,
+ ...filteredParams,
})
);
diff --git a/server/schema/types.ts b/server/schema/types.ts
index 204eddcb..2d22e9de 100644
--- a/server/schema/types.ts
+++ b/server/schema/types.ts
@@ -36,6 +36,14 @@ export const generalTypes = gql`
export const queryTypes = gql`
type Query {
+ getAccountingReport(
+ auth: authType!
+ category: String
+ currency: String
+ fiat: String
+ month: String
+ year: String
+ ): String!
getVolumeHealth(auth: authType!): channelsHealth
getTimeHealth(auth: authType!): channelsTimeHealth
getFeeHealth(auth: authType!): channelsFeeHealth
diff --git a/server/tests/__mocks__/balanceofsatoshis/balances.ts b/server/tests/__mocks__/balanceofsatoshis/balances.ts
new file mode 100644
index 00000000..a4de4ee6
--- /dev/null
+++ b/server/tests/__mocks__/balanceofsatoshis/balances.ts
@@ -0,0 +1,3 @@
+export const getAccountingReport = jest
+ .fn()
+ .mockReturnValue(Promise.resolve({}));
diff --git a/src/components/buttons/multiButton/MultiButton.tsx b/src/components/buttons/multiButton/MultiButton.tsx
index 9c7a39a6..f4660991 100644
--- a/src/components/buttons/multiButton/MultiButton.tsx
+++ b/src/components/buttons/multiButton/MultiButton.tsx
@@ -21,6 +21,7 @@ const StyledSingleButton = styled.button`
background-color: transparent;
color: ${multiSelectColor};
flex-grow: 1;
+ transition: background-color 0.5s ease;
${({ selected, buttonColor }) =>
selected
diff --git a/src/graphql/queries/__generated__/getAccountingReport.generated.tsx b/src/graphql/queries/__generated__/getAccountingReport.generated.tsx
new file mode 100644
index 00000000..d4f3e0ff
--- /dev/null
+++ b/src/graphql/queries/__generated__/getAccountingReport.generated.tsx
@@ -0,0 +1,92 @@
+import gql from 'graphql-tag';
+import * as ApolloReactCommon from '@apollo/react-common';
+import * as ApolloReactHooks from '@apollo/react-hooks';
+import * as Types from '../../types';
+
+export type GetAccountingReportQueryVariables = Types.Exact<{
+ auth: Types.AuthType;
+ category?: Types.Maybe;
+ currency?: Types.Maybe;
+ fiat?: Types.Maybe;
+ month?: Types.Maybe;
+ year?: Types.Maybe;
+}>;
+
+export type GetAccountingReportQuery = { __typename?: 'Query' } & Pick<
+ Types.Query,
+ 'getAccountingReport'
+>;
+
+export const GetAccountingReportDocument = gql`
+ query GetAccountingReport(
+ $auth: authType!
+ $category: String
+ $currency: String
+ $fiat: String
+ $month: String
+ $year: String
+ ) {
+ getAccountingReport(
+ auth: $auth
+ category: $category
+ currency: $currency
+ fiat: $fiat
+ month: $month
+ year: $year
+ )
+ }
+`;
+
+/**
+ * __useGetAccountingReportQuery__
+ *
+ * To run a query within a React component, call `useGetAccountingReportQuery` and pass it any options that fit your needs.
+ * When your component renders, `useGetAccountingReportQuery` returns an object from Apollo Client that contains loading, error, and data properties
+ * you can use to render your UI.
+ *
+ * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
+ *
+ * @example
+ * const { data, loading, error } = useGetAccountingReportQuery({
+ * variables: {
+ * auth: // value for 'auth'
+ * category: // value for 'category'
+ * currency: // value for 'currency'
+ * fiat: // value for 'fiat'
+ * month: // value for 'month'
+ * year: // value for 'year'
+ * },
+ * });
+ */
+export function useGetAccountingReportQuery(
+ baseOptions?: ApolloReactHooks.QueryHookOptions<
+ GetAccountingReportQuery,
+ GetAccountingReportQueryVariables
+ >
+) {
+ return ApolloReactHooks.useQuery<
+ GetAccountingReportQuery,
+ GetAccountingReportQueryVariables
+ >(GetAccountingReportDocument, baseOptions);
+}
+export function useGetAccountingReportLazyQuery(
+ baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
+ GetAccountingReportQuery,
+ GetAccountingReportQueryVariables
+ >
+) {
+ return ApolloReactHooks.useLazyQuery<
+ GetAccountingReportQuery,
+ GetAccountingReportQueryVariables
+ >(GetAccountingReportDocument, baseOptions);
+}
+export type GetAccountingReportQueryHookResult = ReturnType<
+ typeof useGetAccountingReportQuery
+>;
+export type GetAccountingReportLazyQueryHookResult = ReturnType<
+ typeof useGetAccountingReportLazyQuery
+>;
+export type GetAccountingReportQueryResult = ApolloReactCommon.QueryResult<
+ GetAccountingReportQuery,
+ GetAccountingReportQueryVariables
+>;
diff --git a/src/graphql/queries/getAccountingReport.ts b/src/graphql/queries/getAccountingReport.ts
new file mode 100644
index 00000000..313ebef4
--- /dev/null
+++ b/src/graphql/queries/getAccountingReport.ts
@@ -0,0 +1,21 @@
+import gql from 'graphql-tag';
+
+export const GET_ACCOUNTING_REPORT = gql`
+ query GetAccountingReport(
+ $auth: authType!
+ $category: String
+ $currency: String
+ $fiat: String
+ $month: String
+ $year: String
+ ) {
+ getAccountingReport(
+ auth: $auth
+ category: $category
+ currency: $currency
+ fiat: $fiat
+ month: $month
+ year: $year
+ )
+ }
+`;
diff --git a/src/graphql/types.ts b/src/graphql/types.ts
index 84b35e1d..474a27d3 100644
--- a/src/graphql/types.ts
+++ b/src/graphql/types.ts
@@ -45,6 +45,7 @@ export type PermissionsType = {
export type Query = {
__typename?: 'Query';
+ getAccountingReport: Scalars['String'];
getVolumeHealth?: Maybe;
getTimeHealth?: Maybe;
getFeeHealth?: Maybe;
@@ -90,6 +91,15 @@ export type Query = {
getLatestVersion?: Maybe;
};
+export type QueryGetAccountingReportArgs = {
+ auth: AuthType;
+ category?: Maybe;
+ currency?: Maybe;
+ fiat?: Maybe;
+ month?: Maybe;
+ year?: Maybe;
+};
+
export type QueryGetVolumeHealthArgs = {
auth: AuthType;
};
diff --git a/src/utils/helpers.tsx b/src/utils/helpers.tsx
index 98ec4c01..2da6ead4 100644
--- a/src/utils/helpers.tsx
+++ b/src/utils/helpers.tsx
@@ -93,12 +93,16 @@ export const getPercent = (
return Math.round(percent);
};
-export const saveToPc = (jsonData: string, filename: string) => {
+export const saveToPc = (
+ jsonData: string,
+ filename: string,
+ isCsv?: boolean
+) => {
const fileData = jsonData;
const blob = new Blob([fileData], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
- link.download = `${filename}.txt`;
+ link.download = isCsv ? `${filename}.csv` : `${filename}.txt`;
link.href = url;
link.click();
};
diff --git a/src/views/balance/AdvancedBalance.tsx b/src/views/balance/AdvancedBalance.tsx
index ebebe9ce..5b440933 100644
--- a/src/views/balance/AdvancedBalance.tsx
+++ b/src/views/balance/AdvancedBalance.tsx
@@ -231,7 +231,7 @@ export const AdvancedBalance = () => {
{hasAvoid ? : }
-
+
{hasInChannel ? (
{state.in_through.alias}
) : null}
@@ -247,7 +247,7 @@ export const AdvancedBalance = () => {
{!hasOutChannels && (
-
+
{hasOutChannel ? (
{state.out_through.alias}
) : null}
@@ -427,7 +427,14 @@ export const AdvancedBalance = () => {
- {renderButton(() => isDetailedSet(false), 'Auto', !isDetailed)}
+ {renderButton(
+ () => {
+ dispatch({ type: 'clearFilters' });
+ isDetailedSet(false);
+ },
+ 'Auto',
+ !isDetailed
+ )}
{renderButton(() => isDetailedSet(true), 'Detailed', isDetailed)}
diff --git a/src/views/tools/Tools.styled.tsx b/src/views/tools/Tools.styled.tsx
index 6a7d63bf..9da23c6a 100644
--- a/src/views/tools/Tools.styled.tsx
+++ b/src/views/tools/Tools.styled.tsx
@@ -1,4 +1,5 @@
import styled from 'styled-components';
+import { ResponsiveLine } from 'src/components/generic/Styled';
export const NoWrap = styled.div`
margin-right: 16px;
@@ -22,3 +23,7 @@ export const Column = styled.div`
justify-content: center;
align-items: center;
`;
+
+export const ToolsResponsiveLine = styled(ResponsiveLine)`
+ margin-bottom: 8px;
+`;
diff --git a/src/views/tools/WalletVersion.tsx b/src/views/tools/WalletVersion.tsx
index dd91fa96..b9debd80 100644
--- a/src/views/tools/WalletVersion.tsx
+++ b/src/views/tools/WalletVersion.tsx
@@ -7,6 +7,7 @@ import {
Card,
Sub4Title,
Separation,
+ DarkSubTitle,
} from '../../components/generic/Styled';
import { useStatusState } from '../../context/StatusContext';
import { LoadingCard } from '../../components/loading/LoadingCard';
@@ -30,7 +31,10 @@ export const WalletVersion = () => {
if (minorVersion < 10) {
return (
- Update to LND version 0.10.0 or higher to see your wallet build info.
+
+ Update to LND version 0.10.0 or higher to see your wallet build
+ info.
+
);
}
diff --git a/src/views/tools/accounting/Accounting.tsx b/src/views/tools/accounting/Accounting.tsx
new file mode 100644
index 00000000..a3922084
--- /dev/null
+++ b/src/views/tools/accounting/Accounting.tsx
@@ -0,0 +1,221 @@
+import * as React from 'react';
+import {
+ CardWithTitle,
+ SubTitle,
+ Card,
+ SingleLine,
+ DarkSubTitle,
+ Separation,
+} from 'src/components/generic/Styled';
+import { useGetAccountingReportLazyQuery } from 'src/graphql/queries/__generated__/getAccountingReport.generated';
+import { useAccountState } from 'src/context/AccountContext';
+import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
+import {
+ MultiButton,
+ SingleButton,
+} from 'src/components/buttons/multiButton/MultiButton';
+import { X } from 'react-feather';
+import { saveToPc } from 'src/utils/helpers';
+import { ToolsResponsiveLine } from '../Tools.styled';
+
+type ReportType =
+ | 'chain-fees'
+ | 'chain-receives'
+ | 'chain-sends'
+ | 'forwards'
+ | 'invoices'
+ | 'payments';
+// type FiatType = 'eur' | 'usd';
+type YearType = 2017 | 2018 | 2019 | 2020;
+type MonthType = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | null;
+
+type StateType = {
+ type: ReportType;
+ // fiat?: FiatType;
+ year?: YearType;
+ month?: MonthType;
+};
+
+export type ActionType =
+ | {
+ type: 'type';
+ report: ReportType;
+ }
+ // | {
+ // type: 'fiat';
+ // fiat: FiatType;
+ // }
+ | {
+ type: 'year';
+ year: YearType;
+ }
+ | {
+ type: 'month';
+ month: MonthType;
+ };
+
+const initialState: StateType = {
+ type: 'invoices',
+ // fiat: 'eur',
+ year: 2020,
+ month: null,
+};
+
+const reducer = (state: StateType, action: ActionType): StateType => {
+ switch (action.type) {
+ case 'type':
+ return { ...state, type: action.report };
+ // case 'fiat':
+ // return { ...state, fiat: action.fiat };
+ case 'year':
+ return { ...state, year: action.year };
+ case 'month':
+ return { ...state, month: action.month };
+ default:
+ return state;
+ }
+};
+
+export const Accounting = () => {
+ const { auth } = useAccountState();
+ const [showDetails, setShowDetails] = React.useState(false);
+ const [state, dispatch] = React.useReducer(reducer, initialState);
+
+ const [getReport, { data, loading }] = useGetAccountingReportLazyQuery();
+
+ React.useEffect(() => {
+ if (!loading && data && data.getAccountingReport) {
+ saveToPc(
+ data.getAccountingReport,
+ `accounting-${state.type}-${state.year || ''}-${state.month || ''}`,
+ true
+ );
+ }
+ }, [data, loading]);
+
+ const reportButton = (report: ReportType, title: string) => (
+ !loading && dispatch({ type: 'type', report })}
+ >
+ {title}
+
+ );
+
+ // const fiatButton = (fiat: FiatType, title: string) => (
+ // !loading && dispatch({ type: 'fiat', fiat })}
+ // >
+ // {title}
+ //
+ // );
+
+ const yearButton = (year: YearType) => (
+ !loading && dispatch({ type: 'year', year })}
+ >
+ {year}
+
+ );
+
+ const monthButton = (month: MonthType) => (
+ !loading && dispatch({ type: 'month', month })}
+ >
+ {month ? month : 'All'}
+
+ );
+
+ const renderDetails = () => (
+ <>
+
+
+ Type
+
+ {reportButton('chain-fees', 'Chain Fees')}
+ {reportButton('chain-receives', 'Chain Received')}
+ {reportButton('chain-sends', 'Chain Sent')}
+ {reportButton('forwards', 'Forwards')}
+ {/* {reportButton('payments', 'Payments')} */}
+ {reportButton('invoices', 'Invoices')}
+
+
+ {/*
+ Fiat
+
+ {fiatButton('eur', 'Euro')}
+ {fiatButton('usd', 'US Dollar')}
+
+ */}
+
+ Year
+
+ {yearButton(2017)}
+ {yearButton(2018)}
+ {yearButton(2019)}
+ {yearButton(2020)}
+
+
+
+ Month
+
+ {monthButton(null)}
+ {monthButton(1)}
+ {monthButton(2)}
+ {monthButton(3)}
+ {monthButton(4)}
+ {monthButton(5)}
+ {monthButton(6)}
+ {monthButton(7)}
+ {monthButton(8)}
+ {monthButton(9)}
+ {monthButton(10)}
+ {monthButton(11)}
+ {monthButton(12)}
+
+
+
+ getReport({
+ variables: {
+ auth,
+ // fiat: state.fiat,
+ category: state.type,
+ year: state.year.toString(),
+ ...(state.month && { month: state.month.toString() }),
+ },
+ })
+ }
+ fullWidth={true}
+ withMargin={'16px 0 0'}
+ >
+ Generate
+
+ >
+ );
+
+ return (
+
+ Accounting
+
+
+ Report
+
+ showDetails ? setShowDetails(false) : setShowDetails(true)
+ }
+ >
+ {showDetails ? : 'Create'}
+
+
+ {showDetails && renderDetails()}
+
+
+ );
+};