Skip to content

Fix graph tooltip stacking, source the SnowObs token from AFP, move the CSV captcha to Turnstile, spin the CSV download - #1227

Merged
rchlfryn merged 5 commits into
mainfrom
fix/weather-snowobs-zindex
Aug 25, 2026
Merged

Fix graph tooltip stacking, source the SnowObs token from AFP, move the CSV captcha to Turnstile, spin the CSV download#1227
rchlfryn merged 5 commits into
mainfrom
fix/weather-snowobs-zindex

Conversation

@rchlfryn

@rchlfryn rchlfryn commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Description

Four fixes on the weather station pages, one commit each.

Related Issues

None filed — these came out of hands-on use of the station pages.

Key Changes

Chart tooltips stop covering the header (902f0e85)
ECharts hard-codes z-index:9999999 on its tooltip div, so a tooltip on the graphs painted over the site header, the nav dropdowns and any open dialog. tooltip.extraCssText is applied after that default, so z-index: 10 there wins while keeping the tooltip above the chart itself.

SnowObs token comes from the center's AFP config (d9849b4a)
SNOWOBS_TOKEN duplicated what the center's AFP config already publishes at widget_config.stations.token — the same public token the legacy nwac.us widgets read. fetchStationTimeseries now reads it from getAvalancheCenterMetadata(), so there is one source and nothing to set per environment. SNOWOBS_TOKEN is gone (.env.example, Vercel envs); a center whose config carries no token now fails loudly with a SnowObsError. The zod schema already modelled the field, so no schema change was needed.

CSV download captcha is Cloudflare Turnstile (068905e7)
Same shape as the reCAPTCHA it replaces: explicit render (api.js only auto-scans on its first execution, which client-side navigation breaks), the solved token rides the GET form as cf-turnstile-response, and verification only enforces once both keys are set. Turnstile also reports errors rather than only expiry, so the submit button re-locks on either.

The CSV download shows a spinner (f4e8af75)
A year of observations takes a few seconds upstream, and the plain form submit showed nothing while it worked. The form now fetches the CSV and saves the blob, so the button spins and locks while it runs, and a failure says so in place instead of replacing the page with a plain-text error.

How to test

  1. Tooltip — open /weather/stations/alpental, hover a chart, and scroll so the tooltip meets the sticky header: it now passes under the header and under an open nav dropdown instead of over them.
  2. Token — no config needed any more. The station page, the graphs tab and the CSV export were all verified against live SnowObs data with no SNOWOBS_TOKEN set anywhere.
  3. Captcha — set Cloudflare's always-passing test keys locally (site 1x00000000000000000000AA, secret 1x0000000000000000000000000000000AA), open the Download tab, and confirm the widget renders, the submit button unlocks on solve, and the CSV downloads. With either key unset the captcha stays disabled and downloads work as before.
  4. Spinner — on the Download tab pick a year and submit: the button reads "Preparing CSV…" with a spinner and stays disabled until the file lands. Drop your network and submit again to see the inline failure message.

Full suite green: pnpm tsc, pnpm lint, pnpm test (640), pnpm fallow:check, pnpm drift:check.

Screenshots / Demo video

None — the tooltip fix is a stacking change best seen live, and the rest have little visual surface.

Migration Explanation

No migrations.

Future enhancements / Questions

  • Deploy step: Production and Preview need NEXT_PUBLIC_TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY before the captcha is live; NEXT_PUBLIC_RECAPTCHA_SITE_KEY, RECAPTCHA_SECRET_KEY and SNOWOBS_TOKEN can all be deleted. This supersedes the outstanding "get real reCAPTCHA v2 prod keys" item.
  • Dropping the env override means prod depends on api.avalanche.org for the token at each cache miss (24h revalidate) with no local escape hatch. Deliberate — one source of truth was the goal.
  • A Turnstile token is single-use, so a second download in the same session will fail verification until the reader re-solves. Pre-existing (reCAPTCHA behaved the same), now visible as an inline message rather than a plain-text page. Worth a follow-up if people download several years in a row.
  • The token is fetched for the hardcoded nwac slug, which today doubles as the SnowObs source name. Make weather station data pages center-dynamic #1169 splits those when a second center gets these pages.

rchlfryn and others added 3 commits August 25, 2026 13:07
ECharts hard-codes z-index:9999999 on its tooltip div, so a tooltip on the
station graphs painted over the site header, the nav dropdowns and any open
dialog. extraCssText is applied after that default, so it wins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The token was an env var duplicating what the center's AFP config already
publishes at widget_config.stations.token — the same public token the legacy
nwac.us widgets read. Fetch it from there instead, so there is one source and
nothing to set per environment. SNOWOBS_TOKEN is gone; a center without a token
in its config now fails loudly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same shape as the reCAPTCHA it replaces: explicit render (api.js only
auto-scans on its first execution, which client-side navigation breaks), the
solved token rides the GET form as cf-turnstile-response, and verification only
enforces once both keys are set. Turnstile also reports errors, not just
expiry, so the submit button re-locks on either.

Deploys need NEXT_PUBLIC_TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY; the
RECAPTCHA pair can be dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Preview deployment: https://fixxweather-snowobs-zindex.preview.avy-fx.org

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Here's a visual recap of what changed:

Visual recap

Open the full interactive recap

A year of observations takes a few seconds upstream and the form submit showed
nothing while it worked. Fetch the CSV and save the blob instead, so the button
can spin and lock, and a failure says so in place rather than replacing the page
with plain text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rchlfryn rchlfryn changed the title Fix graph tooltip stacking, source the SnowObs token from AFP, move the CSV captcha to Turnstile Fix graph tooltip stacking, source the SnowObs token from AFP, move the CSV captcha to Turnstile, spin the CSV download Aug 25, 2026
@rchlfryn rchlfryn self-assigned this Aug 25, 2026
- Re-arm the captcha after every download attempt. A Turnstile token is
  single-use, and since the page no longer navigates, the spent token was being
  resubmitted — every download after the first would have 403'd once the keys go
  live, with no way back but a reload.
- Keep the download anchor in the document while it is clicked and defer the
  revoke. Firefox ignores a click on a detached anchor, so the save was a silent
  no-op there.
- Fetch the CSV with cache: 'no-store'. The URL repeats exactly and the route
  sets no Cache-Control, so a re-download could be served a stale body.
- Clear a stale failure notice when a selection changes.
- Cover the captcha path in the client test, which the deleted env var had been
  hiding, and drop the type assertion CLAUDE.md forbids from the SnowObs test.
- One 'nwac' literal in the SnowObs service instead of two, and say what
  force-dynamic actually rests on now that the token no longer comes from env.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rchlfryn
rchlfryn added this pull request to the merge queue Aug 25, 2026
Merged via the queue into main with commit 3527bb4 Aug 25, 2026
17 checks passed
@rchlfryn
rchlfryn deleted the fix/weather-snowobs-zindex branch August 25, 2026 23:31
@rchlfryn

Copy link
Copy Markdown
Collaborator Author

🚀 This is included in version v1.16.0

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