Skip to content

Commit

Permalink
feat(earn): add simple stats to earn report (#731)
Browse files Browse the repository at this point in the history
* dev(earn): generate demo report entry in dev mode

* feat(earn): add simple stats to earn report
  • Loading branch information
theborakompanioni authored Mar 29, 2024
1 parent 279ebeb commit 24a9026
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 8 deletions.
142 changes: 136 additions & 6 deletions src/components/EarnReport.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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',
Expand Down Expand Up @@ -224,6 +226,22 @@ const EarnReportTable = ({ data }: EarnReportTableProps) => {
)
}

interface StatsBoxProps {
title: string
value: React.ReactNode
description?: string
}

function StatsBox({ title, value, description }: StatsBoxProps) {
return (
<div className="d-flex flex-1 flex-column border rounded p-3 p-md-4">
<div className="fs-6 text-center">{title}</div>
<div className="fs-4 text-center">{value}</div>
{description ? <div>{description}</div> : null}
</div>
)
}

interface EarnReportProps {
entries: EarnReportEntry[]
refresh: (signal: AbortSignal) => Promise<void>
Expand Down Expand Up @@ -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 (
<div className={styles.earnReportContainer}>
<div className={styles.titleBar}>
Expand Down Expand Up @@ -313,13 +356,43 @@ export function EarnReport({ entries, refresh }: EarnReportProps) {
</rb.Form.Group>
</div>
</div>
<div className="px-md-3 pb-2">
{entries.length === 0 ? (
<div className="px-3 py-3 pt-lg-0">
<div className="d-flex flex-wrap justify-content-around align-items-center gap-2">
<StatsBox
title={t('earn.report.stats.earned_total')}
value={
<Balance valueString={earnedTotal.toString() || ''} convertToUnit={settings.unit} showBalance={true} />
}
/>
<StatsBox
title={t('earn.report.stats.earned_90days')}
value={
<Balance valueString={earned90Days.toString() || ''} convertToUnit={settings.unit} showBalance={true} />
}
/>
<StatsBox
title={t('earn.report.stats.earned_30days')}
value={
<Balance valueString={earned30Days.toString() || ''} convertToUnit={settings.unit} showBalance={true} />
}
/>
<StatsBox
title={t('earn.report.stats.earned_24hours')}
value={
<Balance valueString={earned24Hours.toString() || ''} convertToUnit={settings.unit} showBalance={true} />
}
/>
</div>
</div>
{entries.length === 0 ? (
<div className="px-2">
<rb.Alert variant="info">{t('earn.alert_empty_report')}</rb.Alert>
) : (
</div>
) : (
<div className="px-md-2">
<EarnReportTable data={tableData} />
)}
</div>
</div>
)}
</div>
)
}
Expand All @@ -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<EarnReportEntry[] | null>(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) => {
Expand Down Expand Up @@ -410,6 +519,27 @@ export function EarnReportOverlay({ show, onHide }: rb.OffcanvasProps) {
})
) : (
<>
{__dev_showGenerateDemoReportButton && (
<rb.Row>
<rb.Col className="px-0 mb-2">
<rb.Button
className="position-relative"
variant="outline-dark"
disabled={false}
onClick={() => __dev_generateDemoReportEntryButton()}
>
<div className="d-flex justify-content-center align-items-center">
{t('earn.report.text_button_generate_demo_report')}
<Sprite symbol="plus" width="20" height="20" className="ms-2" />
</div>
<span className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-warning">
dev
</span>
</rb.Button>
</rb.Col>
</rb.Row>
)}

{alert && <rb.Alert variant={alert.variant}>{alert.message}</rb.Alert>}
{entries && (
<rb.Row>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Send/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 2 additions & 0 deletions src/constants/debugFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -22,6 +23,7 @@ const debugFeatures: DebugFeatures = {
rescanChainPage: devMode,
allowFeeValuesReset: devMode,
fastThemeToggle: devMode,
enableDemoEarnReport: devMode,
}

type DebugFeature = keyof DebugFeatures
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 24a9026

Please sign in to comment.