diff --git a/src/components/EarnReport.tsx b/src/components/EarnReport.tsx index 4ed0016f..2a5606ef 100644 --- a/src/components/EarnReport.tsx +++ b/src/components/EarnReport.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table' import { usePagination } from '@table-library/react-table-library/pagination' import { useSort, HeaderCellSort, SortToggleType } from '@table-library/react-table-library/sort' @@ -12,6 +12,8 @@ import Balance from './Balance' import Sprite from './Sprite' import TablePagination from './TablePagination' import styles from './EarnReport.module.css' +import { isDebugFeatureEnabled } from '../constants/debugFeatures' +import { pseudoRandomNumber } from './Send/helpers' const SORT_KEYS = { timestamp: 'TIMESTAMP', @@ -224,6 +226,22 @@ const EarnReportTable = ({ data }: EarnReportTableProps) => { ) } +interface StatsBoxProps { + title: string + value: React.ReactNode + description?: string +} + +function StatsBox({ title, value, description }: StatsBoxProps) { + return ( +
+
{title}
+
{value}
+ {description ?
{description}
: null} +
+ ) +} + interface EarnReportProps { entries: EarnReportEntry[] refresh: (signal: AbortSignal) => Promise @@ -259,6 +277,31 @@ export function EarnReport({ entries, refresh }: EarnReportProps) { return { nodes } }, [entries, search]) + const earnedTotal: Api.AmountSats = useMemo(() => { + return entries.map((entry) => entry.earnedAmount ?? 0).reduce((previous, current) => previous + current, 0) + }, [entries]) + + const earned90Days: Api.AmountSats = useMemo(() => { + return entries + .filter((it) => it.timestamp.getTime() > Date.now() - 90 * 24 * 60 * 60 * 1_000) + .map((it) => it.earnedAmount ?? 0) + .reduce((previous, current) => previous + current, 0) + }, [entries]) + + const earned30Days: Api.AmountSats = useMemo(() => { + return entries + .filter((it) => it.timestamp.getTime() > Date.now() - 30 * 24 * 60 * 60 * 1_000) + .map((it) => it.earnedAmount ?? 0) + .reduce((previous, current) => previous + current, 0) + }, [entries]) + + const earned24Hours: Api.AmountSats = useMemo(() => { + return entries + .filter((it) => it.timestamp.getTime() > Date.now() - 1 * 24 * 60 * 60 * 1_000) + .map((it) => it.earnedAmount ?? 0) + .reduce((previous, current) => previous + current, 0) + }, [entries]) + return (
@@ -313,13 +356,43 @@ export function EarnReport({ entries, refresh }: EarnReportProps) {
-
- {entries.length === 0 ? ( +
+
+ + } + /> + + } + /> + + } + /> + + } + /> +
+
+ {entries.length === 0 ? ( +
{t('earn.alert_empty_report')} - ) : ( +
+ ) : ( +
- )} -
+
+ )} ) } @@ -330,6 +403,42 @@ export function EarnReportOverlay({ show, onHide }: rb.OffcanvasProps) { const [isInitialized, setIsInitialized] = useState(false) const [isLoading, setIsLoading] = useState(true) const [entries, setEntries] = useState(null) + const [__dev_showGenerateDemoReportButton] = useState(isDebugFeatureEnabled('enableDemoEarnReport')) + + const __dev_generateDemoReportEntryButton = () => { + const randomTimestamp = new Date(Date.now() - Date.now() * Math.random() * Math.pow(10, pseudoRandomNumber(-5, -1))) + setEntries((it) => { + const connectedNote = { + timestamp: randomTimestamp, + cjTotalAmount: null, + inputCount: null, + inputAmount: null, + fee: null, + earnedAmount: null, + confirmationDuration: null, + notes: 'Connected ', + } + if (!it || it.length === 0) { + connectedNote.timestamp = new Date(Date.now() - Date.now() * 0.1) + return [connectedNote] + } + if (it.length > 2 && Math.random() > 0.8) { + return [...it, connectedNote] + } + const randomEntry = { + timestamp: randomTimestamp, + cjTotalAmount: Math.round(Math.random() * Math.pow(10, pseudoRandomNumber(7, 9))), + inputCount: Math.max(1, pseudoRandomNumber(-1, 4)), + inputAmount: Math.round(Math.random() * Math.pow(10, pseudoRandomNumber(3, 6))), + fee: Math.round(Math.random() * 100 + 1), + earnedAmount: Math.round(Math.random() * Math.pow(10, pseudoRandomNumber(1, 3)) + 1), + confirmationDuration: Math.round(Math.random() * 100), + notes: null, + } + + return [...it, randomEntry] + }) + } const refresh = useCallback( (signal: AbortSignal) => { @@ -410,6 +519,27 @@ export function EarnReportOverlay({ show, onHide }: rb.OffcanvasProps) { }) ) : ( <> + {__dev_showGenerateDemoReportButton && ( + + + __dev_generateDemoReportEntryButton()} + > +
+ {t('earn.report.text_button_generate_demo_report')} + +
+ + dev + +
+
+
+ )} + {alert && {alert.message}} {entries && ( diff --git a/src/components/Send/helpers.ts b/src/components/Send/helpers.ts index b284c2f3..703244a5 100644 --- a/src/components/Send/helpers.ts +++ b/src/components/Send/helpers.ts @@ -10,7 +10,7 @@ export const initialNumCollaborators = (minValue: number) => { } // not cryptographically random. returned number is in range [min, max] (both inclusive). -const pseudoRandomNumber = (min: number, max: number) => { +export const pseudoRandomNumber = (min: number, max: number) => { return Math.round(Math.random() * (max - min)) + min } diff --git a/src/constants/debugFeatures.ts b/src/constants/debugFeatures.ts index ce284b34..06ea5781 100644 --- a/src/constants/debugFeatures.ts +++ b/src/constants/debugFeatures.ts @@ -8,6 +8,7 @@ interface DebugFeatures { rescanChainPage: boolean allowFeeValuesReset: boolean fastThemeToggle: boolean + enableDemoEarnReport: boolean } const devMode = process.env.NODE_ENV === 'development' && process.env.REACT_APP_JAM_DEV_MODE === 'true' @@ -22,6 +23,7 @@ const debugFeatures: DebugFeatures = { rescanChainPage: devMode, allowFeeValuesReset: devMode, fastThemeToggle: devMode, + enableDemoEarnReport: devMode, } type DebugFeature = keyof DebugFeatures diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 861aa5ff..f32e6e2b 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -421,7 +421,14 @@ "heading_input_count": "My Inputs", "heading_input_value": "My Input Amount", "heading_earned": "Earned", - "heading_notes": "Notes" + "heading_notes": "Notes", + "text_button_generate_demo_report": "Generate demo entry", + "stats": { + "earned_total": "Earned (total)", + "earned_90days": "Earned (90 days)", + "earned_30days": "Earned (30 days)", + "earned_24hours": "Earned (24 hours)" + } }, "title_fidelity_bonds_zero": "Create a Fidelity Bond", "title_fidelity_bonds_one": "Your Fidelity Bond",