feat: ZIP → rep lookup, member page redesign, letter improvements
ZIP lookup (GET /api/members/by-zip/{zip}):
- Two-step geocoding: Nominatim (ZIP → lat/lng) then Census TIGERweb
Legislative identify (lat/lng → congressional district via GEOID)
- Handles at-large states (AK, DE, MT, ND, SD, VT, WY)
- Added rep_lookup health check to admin External API Health panel
congress_api.py fixes:
- parse_member_from_api: normalize state full name → 2-letter code
(Congress.gov returns "Florida", DB expects "FL")
- parse_member_from_api: read district from top-level data field,
not current_term (district is not inside the term object)
Celery beat: schedule sync_members daily at 1 AM UTC so chamber,
district, and contact info stay current without manual triggering
Members page redesign: photo avatars, party/state/chamber chips,
phone + website links, ZIP lookup form to find your reps
Draft letter improvements: pass rep_name from ZIP lookup so letter
opens with "Dear Representative Franklin," instead of generic salutation;
add has_document filter to bills list endpoint
UX additions: HelpTip component, How It Works page, "How it works"
sidebar nav link, collections page description copy
Authored-By: Jack Levy
This commit is contained in:
@@ -5,6 +5,7 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import Link from "next/link";
|
||||
import { Bookmark, Plus, Globe, Lock, Trash2 } from "lucide-react";
|
||||
import { collectionsAPI } from "@/lib/api";
|
||||
import { HelpTip } from "@/components/shared/HelpTip";
|
||||
import type { Collection } from "@/lib/types";
|
||||
|
||||
export default function CollectionsPage() {
|
||||
@@ -49,9 +50,15 @@ export default function CollectionsPage() {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bookmark className="w-5 h-5 text-primary" />
|
||||
<h1 className="text-xl font-bold">My Collections</h1>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Bookmark className="w-5 h-5 text-primary" />
|
||||
<h1 className="text-xl font-bold">My Collections</h1>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
A collection is a named group of bills you curate — like a playlist for legislation.
|
||||
Share any collection via a link; no account required to view.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowForm((v) => !v)}
|
||||
@@ -87,7 +94,7 @@ export default function CollectionsPage() {
|
||||
className="rounded"
|
||||
/>
|
||||
<span>Public collection</span>
|
||||
<span className="text-xs text-muted-foreground">(share link works either way)</span>
|
||||
<HelpTip content="Share links work whether or not a collection is public. Marking it public signals it may appear in a future public directory — private collections are invisible to anyone without your link." />
|
||||
</label>
|
||||
{formError && <p className="text-xs text-destructive">{formError}</p>}
|
||||
<div className="flex gap-2">
|
||||
|
||||
Reference in New Issue
Block a user