Skip to content

Show state legislators with photos and contact info on the council page - #139

Closed
HaydenCash716 wants to merge 10 commits into
FoggedLens:masterfrom
HaydenCash716:legislator-cards
Closed

Show state legislators with photos and contact info on the council page#139
HaydenCash716 wants to merge 10 commits into
FoggedLens:masterfrom
HaydenCash716:legislator-cards

Conversation

@HaydenCash716

@HaydenCash716 HaydenCash716 commented Aug 6, 2026

Copy link
Copy Markdown

Builds on #138 (marked draft until that lands — only the last commit is new here).

#138 gives people links to go find their officials. This goes a step further for the one level of government where open data actually exists: after a search, the card shows your state legislators as people — official photo, name, party, chamber and district, with tap-to-email and tap-to-call. Less friction between "I'm angry about this" and "I sent the email."

Why state and not city council: council data with photos/emails doesn't exist in any open API — Google Civic's representatives endpoint was the only national source and it shut down in April 2025, and the alternatives are paid (Cicero) or stale (Wikidata mayors). Open States is the canonical open source for state legislators and it's good: maintained, official headshots, emails, office phones, queryable by point. I left a thought about the council side over on #136.

How it's wired

  • New OpenStatesClient in the API, same shape as the other upstream clients: env-keyed (OPENSTATES_API_KEY), disk-cached like the Nominatim client (24h, keyed by coordinates — input is city centroids, so repeat cities never hit the upstream and the free-tier rate limit should be a non-issue), errors through the OTel logger. It maps the upstream response down to a slim typed shape rather than passing Open States' full payload through.
  • GET /officials?lat&lng — returns {legislators: [...]}. Without a key configured it just 404s and the frontend silently falls back to the legislator link from Add a local-officials lookup to the council page #138, so merging this changes nothing until you decide to add a key (free, ~2 min at https://open.pluralpolicy.com/accounts/profile/). Same pattern as the other env-gated features.
  • Frontend: legislator fetch runs in parallel with the website lookup, cards only render when data comes back. Party is shown as plain text, no partisan color-coding. Photos lazy-load from Open States with referrerpolicy="no-referrer" and fall back to an icon; attribution line links to openstates.org. The privacy caption now mentions Open States.

Verifying

I don't have an Open States key of my own, so the upstream is exercised two ways:

  • bun test — 6 new tests on the client (response mapping, capitol-office phone preference, missing-field tolerance, request shape, cache behavior, non-OK status), stubbed fetch like GithubClient.test.ts. Whole api suite: 11 pass.
  • Ran the real server with global fetch stubbed to return a fixture for v3.openstates.org (via bun --preload, stub not committed) and drove the page in a browser: cards render with photos, no-photo people get the icon fallback, the mailto/tel links carry the right addresses, and restarting without the key drops cleanly back to the Add a local-officials lookup to the council page #138 links.
  • bun run build-only passes; type-check unchanged (same pre-existing master failures).

If you'd rather not hold an API key at all, the other shape this could take is a baked pipeline like alpr-counts — cron pulls Open States bulk data into a district-keyed JSON on the CDN and the client resolves districts via the Census geocoder. Happy to rework it that way instead if you prefer; the live-proxy version was the smaller diff.

@HaydenCash716

Copy link
Copy Markdown
Author

Folding this into #138 — it's really one feature, and a single PR is easier to review. All commits from this branch are there.

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