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",