You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Day-bucketing/aggregation logic is copy-pasted independently across QuickStats, HistoryChart, and ActivityChart — fixing #29 in History won't fix Dashboard's identical copy #47
The exact same "bucket transactions by day and sum sent/received/fees" logic is independently written out, by hand, three separate times — in dashboard/QuickStats.tsx, pages/History.tsx's HistoryChart, and pages/Dashboard.tsx's ActivityChart — instead of existing once as a shared utility.
These two blocks are the same algorithm with the same month/day-only (no year) key format, the same if (days[key]) truthy-check pattern, and the same reliance on the local timezone via toLocaleDateString.
Why it matters
#29 in this repo, already filed, fixes (or will fix) the year-boundary bucket-collision bug for History.tsx's copy specifically. But Dashboard.tsx's ActivityChart has the exact samemonth/day-only key construction (d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), no year) and will still silently merge two same-calendar-day-different-year transactions into one bucket after#29 is fixed, because it's an entirely independent copy of the logic, not a shared function. Fixing the bug in one file does nothing for the other two. The same is true for any future fix to the bucketing approach in general (e.g. switching to UTC-based bucketing, or fixing the "average fee should only count sent transactions" issue filed separately in this batch, which also affects QuickStats.tsx and History.tsx's HistorySummary independently) — every fix has to be applied three times, in three files, and it's easy to catch two of the three and miss the third, exactly as would happen here.
This is the same "same problem solved three independent, drifting ways" shape as the already-filed isValidStellarAddress/truncateAddress/formatAmount duplication (#18) and the formatDate duplication (#28) in this repo, but for the app's transaction-aggregation/analytics logic specifically, which is materially more complex (and therefore more bug-prone) than a one-line formatter.
Reproduction
grep -n "toLocaleDateString('en-US', { month: 'short', day: 'numeric'" src/pages/Dashboard.tsx src/pages/History.tsx — two independent matches, same format string, same lack of year.
Apply the fix described in issue History page's 30-day volume chart buckets transactions by month/day only, silently merging entries across year boundaries #29 (add year to the bucket key) only to src/pages/History.tsx. Feed Dashboard.tsx's ActivityChart two transactions a year apart on the same calendar day (well within its own 7-day-window fetch of useRecentTransactions(50), which can include transactions from any time in the past depending on account history) — they still silently merge into a single bar, because the fix was never applied to ActivityChart's own copy.
Suggested fix
Extract a single shared function, e.g. bucketTransactionsByDay(transactions, windowDays, { includeFees }) in src/lib/ or src/utils/, that both Dashboard.tsx and History.tsx (and QuickStats.tsx's totals, which do the same "reduce sent/received from a transaction list" work without the day-bucketing part) call into, so any correctness fix — the year-boundary bug, timezone handling, or the sent-vs-received fee-averaging bug — only needs to be made once and benefits all three call sites simultaneously.
Additional Notes
src/pages/Dashboard.tsx:24-52 (ActivityChart), src/pages/History.tsx:23-52 (HistoryChart), src/components/dashboard/QuickStats.tsx:8-29 (same-shape sent/received/fee reduction, no day-bucketing but same underlying duplicated pattern).
Testing strategy: once extracted, a single test suite against the shared bucketing function (year-boundary fixture, timezone fixture, sent-only-fee fixture) covers all three current call sites at once — today, each would need its own duplicate test suite to get the same coverage, and none currently has one.
Problem
The exact same "bucket transactions by day and sum sent/received/fees" logic is independently written out, by hand, three separate times — in
dashboard/QuickStats.tsx,pages/History.tsx'sHistoryChart, andpages/Dashboard.tsx'sActivityChart— instead of existing once as a shared utility.pages/Dashboard.tsx'sActivityChart(7-day window):pages/History.tsx'sHistoryChart(30-day window) — the subject of the already-filed year-boundary collision bug (#29 in this repo):These two blocks are the same algorithm with the same
month/day-only (no year) key format, the sameif (days[key])truthy-check pattern, and the same reliance on the local timezone viatoLocaleDateString.Why it matters
#29in this repo, already filed, fixes (or will fix) the year-boundary bucket-collision bug forHistory.tsx's copy specifically. ButDashboard.tsx'sActivityCharthas the exact samemonth/day-only key construction (d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), no year) and will still silently merge two same-calendar-day-different-year transactions into one bucket after #29 is fixed, because it's an entirely independent copy of the logic, not a shared function. Fixing the bug in one file does nothing for the other two. The same is true for any future fix to the bucketing approach in general (e.g. switching to UTC-based bucketing, or fixing the "average fee should only count sent transactions" issue filed separately in this batch, which also affectsQuickStats.tsxandHistory.tsx'sHistorySummaryindependently) — every fix has to be applied three times, in three files, and it's easy to catch two of the three and miss the third, exactly as would happen here.This is the same "same problem solved three independent, drifting ways" shape as the already-filed
isValidStellarAddress/truncateAddress/formatAmountduplication (#18) and theformatDateduplication (#28) in this repo, but for the app's transaction-aggregation/analytics logic specifically, which is materially more complex (and therefore more bug-prone) than a one-line formatter.Reproduction
grep -n "toLocaleDateString('en-US', { month: 'short', day: 'numeric'" src/pages/Dashboard.tsx src/pages/History.tsx— two independent matches, same format string, same lack of year.src/pages/History.tsx. FeedDashboard.tsx'sActivityCharttwo transactions a year apart on the same calendar day (well within its own 7-day-window fetch ofuseRecentTransactions(50), which can include transactions from any time in the past depending on account history) — they still silently merge into a single bar, because the fix was never applied toActivityChart's own copy.Suggested fix
bucketTransactionsByDay(transactions, windowDays, { includeFees })insrc/lib/orsrc/utils/, that bothDashboard.tsxandHistory.tsx(andQuickStats.tsx's totals, which do the same "reduce sent/received from a transaction list" work without the day-bucketing part) call into, so any correctness fix — the year-boundary bug, timezone handling, or the sent-vs-received fee-averaging bug — only needs to be made once and benefits all three call sites simultaneously.Additional Notes
src/pages/Dashboard.tsx:24-52(ActivityChart),src/pages/History.tsx:23-52(HistoryChart),src/components/dashboard/QuickStats.tsx:8-29(same-shape sent/received/fee reduction, no day-bucketing but same underlying duplicated pattern).Dashboard.tsx's independent copy) and with the "Average Fee includes received transactions' fees" issue filed separately in this batch (same duplication shape, different specific defect).