Skip to content

Dashboard StatCard trend percentages are hardcoded literals, not computed from any real data #92

Description

@chonilius

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 trend literal 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 >= 0 in StatCard.tsx correctly 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 trend props if a similar hardcoded-but-presented-as-computed value is found elsewhere during the audit — document the finding either way).

Acceptance Criteria

  • trend on every dashboard StatCard that 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) — trend should be undefined in that case, not a fabricated placeholder.

Additional Notes

Precise references:

  • src/app/dashboard/contributor/page.tsx:141,151 — the two hardcoded trend literals (12, 8).
  • src/app/dashboard/sponsor/page.tsx:141 — the third hardcoded trend literal (18).
  • src/lib/mock-data.ts:255-256 (contributorEarningsHistory, sponsorSpendHistory) and :258 (contributorSparkline) — the exact historical arrays already passed to these same StatCards as sparkline, sitting right next to the hardcoded trend prop, unused for it.
  • src/components/ui/StatCard.tsx:137,248-264trendUp = typeof trend === "number" && trend >= 0 and 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}).

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third CampaignarchitectureArchitecture/design issuebugSomething isn't workingvery hardVery difficult task, expert-level effort required

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions