Skip to content

Add ForecastNeedle gauge and use it in the forecast cards#135

Merged
eswan18 merged 12 commits into
mainfrom
feature/forecast-needle
Jun 9, 2026
Merged

Add ForecastNeedle gauge and use it in the forecast cards#135
eswan18 merged 12 commits into
mainfrom
feature/forecast-needle

Conversation

@eswan18

@eswan18 eswan18 commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a cartoon-styled half-wheel needle gauge for visualizing forecast probabilities, and wires it into both forecast cards.

What's new

  • ForecastNeedle (components/ui/forecast-needle.tsx) — an SVG gauge with a red→white→green arc and a blocky outlined needle. API is forecast (0–1, primary needle) plus an optional baseline (drawn as a muted "ghost" needle). Hovering/tapping a needle reveals its %; it animates in on mount. Pure geometry helpers are unit-tested.
  • ForecastCard (display) — replaces the colored % box and the you/avg comparison bar with the needle (forecast + community-average ghost) and a small % / Average: NN% readout. The whole card is now a link to the prop details.
  • EditableForecastCard — replaces the % box and slider with the needle plus a number-entry box (type 0–100, Enter/blur to commit). The admin edit pencil now sits inline beside the prop text, and long notes wrap. Real save logic (createForecast/updateForecast), admin gating, and PropEditDialog are unchanged.
  • Storybook — stories for the needle and both cards, plus Storybook-only mocks (.storybook/mocks/) for the server actions and next/link so the Next.js-coupled cards render in the react-vite Storybook without pulling in the DB client.

Both card components keep their existing props, so call sites are untouched.

Behavior changes worth noting

  • The display card navigates to the prop details when clicked (the external-link icon is gone).
  • The editable card now shows the community average, and the slider is removed — the number box is the sole input (the needle isn't draggable).

Testing

  • npm run lint — 0 errors
  • npx tsc --noEmit — clean
  • npm run test — 203 passed
  • npm run build-storybook — passes

🤖 Generated with Claude Code

eswan18 and others added 12 commits June 8, 2026 17:13
A cartoon-styled semicircle "needle" gauge (à la the NYT election needle)
for visualizing forecast probabilities. Supports one or more independently
colored needles on the same half-wheel, e.g. a user's forecast alongside a
community-average baseline.

- Red -> white -> green gradient arc with a dark outline and blocky needles
- `color` accepts any CSS color string (Tailwind var, chart token, or hex);
  defaults to the theme --primary
- Smooth sweep-in animation; 0% / 100% axis labels
- Per-needle label + percentage surfaced in a hover/tap tooltip
- Pure, exported geometry helpers with unit tests; Storybook stories

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Cartoon styling: red -> white -> green gradient, blocky pencil needle and
  hub, dark ink outline
- Sub-semicircle sweep (140deg) so the gauge is taller and less wide
- Render the band as a horizontally-sliced ring (filled + single outline) so
  the ends are flat and parallel to the x-axis with a continuous outline
- Per-needle labels moved into the hover/tap tooltip; axis labels trimmed to
  0% / 100% and enlarged
- Geometry parameterized via SWEEP_DEGREES; tests updated accordingly

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the arbitrary `needles` array with a required `forecast` and an
optional `baseline`, matching the only two real use cases. Styling is now
fixed by role rather than caller-controlled:

- forecast: solid primary needle with an ink outline
- baseline: lighter outline-only "ghost" needle in muted gray, drawn behind

Tooltip labels default to "You" / "Avg" and stay overridable for non-average
baselines. Stories updated to the new API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stories cover the default (has-forecast), empty, low/high probability, and
long-text states. The slider and percent input are interactive.

To render a component that module-imports server actions, alias
@/lib/db_actions and @/lib/db_actions/props to success-returning stubs in
Storybook only, so the Postgres client and next/cache stay out of the bundle.
Shared mock data lives in forecast-card.fixtures.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stories cover the color scale, the you-vs-avg comparison bar (including the
identical and label-collision cases), the no-forecast placeholder, status
badges, and markdown/long-text rendering.

ForecastCard imports next/link, whose app-router internals trip a
circular-import TDZ under Vite's dev transform. Mock next/link to a plain
anchor in Storybook (as @storybook/nextjs does) to render it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Throwaway mockups exploring how to fold the ForecastNeedle into the forecast
card: NeedleRight (compact needle as a right-hand summary), NeedleHero (needle
as the left hero panel), and NeedleTile (centered redesign). Shown side by side
in a Storybook gallery for picking a direction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the NeedleHero and NeedleTile mockups; keep only NeedleRight, the chosen
direction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the colored % box and the you/avg comparison bar with the needle
layout chosen from the experiments: the user's forecast as the primary needle
with the community average as a muted ghost needle, a bold % readout, and an
"Average: NN%" line. The whole card is now a link to the prop details (the
external-link button is gone), and the badges sit top-left.

Same { prop, showCommunityAvg } API, so call sites are unchanged. Delete the
throwaway needle-experiments files; drop the comparison-bar collision stories.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Throwaway mock for folding the ForecastNeedle into the editable forecast card:
the needle (live preview + community-average ghost) on the right with a number
box as the input, an inline edit pencil beside the prop text, and the
unsaved-changes ring + Save/Cancel. Local state only — saving is faked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Notes used `truncate` (nowrap) and spilled off the side; switch to wrapping.
Add LongPropText and LongNotes stories to exercise overflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the colored % box and slider with the needle layout: the user's
forecast as the primary needle plus the community average as a muted ghost
needle, with a number-entry box (type 0–100, Enter/blur to commit) as the
input. The admin edit pencil now sits inline beside the prop text, and notes
wrap instead of truncating.

Real save logic (createForecast/updateForecast via useServerAction), admin
gating, and PropEditDialog are unchanged; same { prop, onForecastUpdate } API,
so the call site is untouched. Delete the throwaway needle experiments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set outputFileTracingRoot and turbopack.root to this project so Next/Turbopack
don't infer a parent directory as the workspace root. On a nested checkout that
mis-rooting poisoned the build/cache and made webpack (via @sentry/nextjs) try
to resolve `tailwindcss` from the parent folder, breaking `next dev`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@eswan18 eswan18 merged commit fab3167 into main Jun 9, 2026
1 check passed
@eswan18 eswan18 deleted the feature/forecast-needle branch June 9, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant