Repository navigation
Dashboard StatCard trend percentages are hardcoded literals, not computed from any real data #92
Description
Overview
Every dashboard StatCard that shows a "vs last period" trend indicator is passing a hardcoded literal number, not a value derived from any real data:
src/app/dashboard/contributor/page.tsx:
<StatCard
label="Lifetime earnings"
value={stats?.lifetimeEarnings}
format="currency"
status={fetchStatus}
icon={DollarSign}
trend={fetchStatus === "loaded" ? 12 : undefined}
sparkline={fetchStatus === "loaded" ? contributorEarningsHistory : undefined}
zeroLabel="No earnings yet"
/>
<StatCard
label="Merged PRs"
value={stats?.mergedPRs}
format="count"
status={fetchStatus}
icon={GitMerge}
trend={fetchStatus === "loaded" ? 8 : undefined}
sparkline={fetchStatus === "loaded" ? contributorSparkline : undefined}
zeroLabel="No merged PRs yet"
/>src/app/dashboard/sponsor/page.tsx:
<StatCard
label="Total paid out"
value={totalSpent}
format="currency"
status={fetchStatus}
icon={Receipt}
trend={fetchStatus === "loaded" ? 18 : undefined}
sparkline={fetchStatus === "loaded" ? sponsorSpendHistory : undefined}
/>12, 8, and 18 are literal numeric constants — the only thing determining whether trend is shown at all is fetchStatus === "loaded"; the literal value itself never varies with stats, totalSpent, or anything else fetched. This is trivially demonstrable: the exact same contributorEarningsHistory/sponsorSpendHistory arrays these StatCards already receive as their sparkline prop (8 weeks of real, varying numbers — [420, 560, 310, 780, 640, 890, 720, 1050] for contributor earnings) sit right there, unused for the trend calculation, which a real implementation would derive from (e.g. comparing the most recent value against the prior period).
The consequence: StatCard's trend indicator — a green/rose arrow plus "X% vs last period," visually presented as real, computed analytics — is fabricated for every dashboard, in every state, including a fully-authenticated user viewing their own real, live data under a "Live data" badge (fetchStatus === "loaded" is true for both the live-fetch-succeeded case and the signed-out demo-data case, so the same hardcoded 12/8/18 shows regardless of which). A contributor whose actual earnings dropped week-over-week still sees a green up-arrow claiming "+12% vs last period," because the number was never computed from their data in the first place — it's a static prop value that happens to always render as if things are trending up (all three hardcoded values are positive, so trendUp = trend >= 0 is always true for every dashboard, every user, always).
Requirements
- Replace each hardcoded
trendliteral with a real computation derived from the same historical data already available at each call site (e.g.contributorEarningsHistory,sponsorSpendHistory) — a straightforward last-period-vs-prior-period percentage change, or whatever comparison the design intends (week-over-week, this-period-vs-last-N-periods-average, etc. — pick and document one). - For the values sourced from a live backend fetch (once the separate "dashboard widgets permanently hardcoded to mock data" issue in this batch is resolved and real history data is available), compute the trend from that live history rather than the static mock arrays — this issue's fix should not depend on that other issue landing first, but should be structured so the trend computation naturally works with either data source once both are addressed.
- Ensure the computed trend can be negative — verify
trendUp = typeof trend === "number" && trend >= 0inStatCard.tsxcorrectly renders the down/rose-colored arrow for a genuinely negative computed trend, since this path has likely never been exercised given every current caller always passes a positive literal. - Audit for any other hardcoded-looking numeric prop passed to a "real-looking" UI element across the dashboards (this fix should not stop at just these three
trendprops if a similar hardcoded-but-presented-as-computed value is found elsewhere during the audit — document the finding either way).
Acceptance Criteria
-
trendon every dashboardStatCardthat shows one is computed from real underlying data, not a literal constant. - A contributor/sponsor whose most recent period's value is lower than the prior period sees a correctly negative, down-arrow trend — verified with a test fixture where the most recent history value is deliberately lower than the previous one.
- A contributor/sponsor whose most recent period's value is higher than the prior period sees a correctly positive, up-arrow trend, matching today's visual behavior for the common case in the mock data.
- The chosen trend-computation method (which periods are compared, how) is documented in a code comment at each call site or in a shared helper.
- No dashboard StatCard displays a trend percentage when there isn't enough historical data to compute one meaningfully (e.g. fewer than 2 data points) —
trendshould beundefinedin that case, not a fabricated placeholder.
Additional Notes
Precise references:
src/app/dashboard/contributor/page.tsx:141,151— the two hardcodedtrendliterals (12,8).src/app/dashboard/sponsor/page.tsx:141— the third hardcodedtrendliteral (18).src/lib/mock-data.ts:255-256(contributorEarningsHistory,sponsorSpendHistory) and:258(contributorSparkline) — the exact historical arrays already passed to these sameStatCards assparkline, sitting right next to the hardcodedtrendprop, unused for it.src/components/ui/StatCard.tsx:137,248-264—trendUp = typeof trend === "number" && trend >= 0and the rendering branch; confirmed the down/rose-arrow code path exists and is presumably correct, just never exercised by any current caller since every literal passed today is positive.
Edge cases: deciding the comparison window matters — "last 8 weeks" sparkline data compared how (last week vs. the week before, or last week vs. an 8-week average) will produce meaningfully different numbers and should be a deliberate choice, not an implementation detail buried without comment. Also worth deciding whether a trend of exactly 0% should show as "up" (current trendUp = trend >= 0 treats zero as up) or as a distinct neutral state — this is a pre-existing minor ambiguity in StatCard itself, not newly introduced by this fix, but worth a one-line acknowledgment in the PR since a real, computed trend is now far more likely to actually land on exactly zero than a hand-picked positive literal ever was.
Test/reproduction plan: unit-test a new computeTrend(history: number[]): number | undefined-style helper (or equivalent inline logic, tested at the page level) with fixtures: a history array trending up (assert positive result), trending down (assert negative result), flat (assert 0 or undefined per the documented decision), and too short to compare (assert undefined). Then assert the dashboard pages pass this computed value into StatCard's trend prop instead of a literal, and that StatCard itself correctly renders the down-arrow/rose styling for a negative fixture (extending StatCard.test.tsx's existing trend-rendering test, which today only exercises a positive trend={12}).
Activity
yunus-dev-codecrafter commentedon Aug 16, 2026
Hi! I've carefully reviewed the entire task description and fully understand the requirements and expected outcome.
I have experience working on similar tasks and I'm confident I can complete this within about an hour. I'd be happy to take ownership of this issue and submit a clean, well-tested solution promptly.
If it's available, please assign it to me. Thank you!
eulami commentedon Aug 16, 2026
Hi. I can submit an efficient PR for this issue within 24 hours. Kindly assign it to me.
annieolivia61-ux commentedon Aug 16, 2026
I will love to solve this issue
grantfox-oss commentedon Aug 16, 2026
🦊 GrantFox — @annieolivia61-ux has been assigned to this issue as part of the Third Campaign campaign!
Next steps:
Closes #92)Good luck! Track your progress on GrantFox.