13 Commits

Author SHA1 Message Date
Jack Levy
9633b4dcb8 feat: v1.0.0 — UX polish, security hardening, code quality
UI/UX:
- Bill detail page tab UI (Analysis / Timeline / Votes / Notes)
- Topic tag pills on bill detail and listing pages — filtered to known
  topics, clickable, properly labelled via shared lib/topics.ts
- Notes panel always-open in Notes tab; sign-in prompt for guests
- Collapsible sidebar with icon-only mode and localStorage persistence
- Bills page defaults to has-text filter enabled
- Follow mode dropdown transparency fix
- Favicon (Landmark icon, blue background)

Security:
- Fernet encryption for ntfy passwords at rest (app/core/crypto.py)
- Separate ENCRYPTION_SECRET_KEY env var; falls back to JWT derivation
- ntfy_password no longer returned in GET response — replaced with
  ntfy_password_set: bool; NotificationSettingsUpdate type for writes
- JWT_SECRET_KEY fail-fast on startup if using default placeholder
- get_optional_user catches (JWTError, ValueError) only, not Exception

Bug fixes & code quality:
- Dashboard N+1 topic query replaced with single OR query
- notification_utils.py topic follower N+1 replaced with batch query
- Note query in bill detail page gated on token (enabled: !!token)
- search.py max_length=500 guard against oversized queries
- CollectionCreate.validate_name wired up with @field_validator
- LLM_RATE_LIMIT_RPM default raised from 10 to 50

Authored by: Jack Levy
2026-03-15 01:10:31 -04:00
Jack Levy
d0da0b8dce feat: Member Effectiveness Score + Representation Alignment View (v0.9.9)
Member Effectiveness Score
- New BillCosponsor table (migration 0018) with per-bill co-sponsor
  party data required for the bipartisan multiplier
- bill_category column on Bill (substantive | commemorative | administrative)
  set by a cheap one-shot LLM call after each brief is generated
- effectiveness_score / percentile / tier columns on Member
- New bill_classifier.py worker with 5 tasks:
    classify_bill_category  — triggered from llm_processor after brief
    fetch_bill_cosponsors   — triggered from congress_poller on new bill
    calculate_effectiveness_scores — nightly at 5 AM UTC
    backfill_bill_categories / backfill_all_bill_cosponsors — one-time
- Scoring: distance-traveled pts × bipartisan (1.5×) × substance (0.1×
  for commemorative) × leadership (1.2× for committee chairs)
- Percentile normalised within (seniority tier × party) buckets
- Effectiveness card on member detail page with colour-coded bar
- Admin panel: 3 new backfill/calculate controls in Maintenance section

Representation Alignment View
- New GET /api/alignment endpoint: cross-references user's stanced bill
  follows (pocket_veto/pocket_boost) with followed members' vote positions
- Efficient bulk queries — no N+1 loops
- New /alignment page with ranked member list and alignment bars
- Alignment added to sidebar nav (auth-required)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 18:05:09 -04:00
Jack Levy
48771287d3 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
2026-03-02 15:47:46 -05:00
Jack Levy
9e5ac9b33d feat: collections, watchlists, and shareable links (v0.9.0)
Phase 3 completion — Personal Workflow feature set is now complete.

Collections / Watchlists:
- New tables: collections (UUID share_token, slug, public/private) and
  collection_bills (unique bill-per-collection constraint)
- Full CRUD API at /api/collections with bill add/remove endpoints
- Public share endpoint /api/collections/share/{token} (no auth)
- /collections list page with inline create form and delete
- /collections/[id] detail page: inline rename, public toggle,
  copy-share-link, bill search/add/remove
- CollectionPicker bookmark-icon popover on bill detail pages
- Collections nav link in sidebar (auth-required)

Shareable Brief Links:
- share_token UUID column on bill_briefs (backfilled on migration)
- Unified public share router at /api/share (brief + collection)
- /share/brief/[token] — minimal layout, full AIBriefCard, CTAs
- /share/collection/[token] — minimal layout, bill list, CTA
- Share2 button in BriefPanel header row, "Link copied!" flash

AuthGuard: /collections → AUTH_REQUIRED; /share prefix → NO_SHELL_PATHS

Authored-By: Jack Levy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 23:23:45 -05:00
Jack Levy
a0e7ab4cd3 feat(ux): welcome banner, dashboard auth fix, docs update
- WelcomeBanner.tsx: guest-only dismissible onboarding card on dashboard
  (localStorage pv_seen_welcome, Browse Bills CTA, X dismiss)
- useDashboard: add !!token to query key so login/logout triggers
  a fresh fetch without manual refresh
- ARCHITECTURE.md: WelcomeBanner component, auth-aware query keys,
  v0.6.1 feature history entry
- Roadmap: mark welcome banner items complete
- Add MVP planning notes (Phase 3-6 roadmap draft)

Co-Authored-By: Jack Levy
2026-03-01 21:22:16 -05:00
Jack Levy
dc5e756749 feat(email_gen): draft constituent letter generator + bill text indicators
- Add DraftLetterPanel: collapsible UI below BriefPanel for bills with a
  brief; lets users select up to 3 cited points, pick stance/tone, and
  generate a plain-text letter via the configured LLM provider
- Stance pre-fills from follow mode (pocket_boost → YES, pocket_veto → NO)
  and clears when the user unfollows; recipient derived from bill chamber
- Add POST /api/bills/{bill_id}/draft-letter endpoint with proper LLM
  provider/model resolution from AppSetting (respects Settings page choice)
- Add generate_text() to LLMProvider ABC and all four providers
- Expose has_document on BillSchema (list endpoint) via a single batch
  query; BillCard shows Brief / Pending / No text indicator per bill

Authored-By: Jack Levy
2026-03-01 16:37:52 -05:00
Jack Levy
ddd74a02d5 feat(public_page): allow unauthenticated browsing with auth-gated interactivity
- Add get_optional_user dependency; dashboard returns guest-safe payload
- AuthGuard only redirects /following and /notifications for guests
- Sidebar hides auth-required nav items and shows Sign In/Register for guests
- Dashboard shows trending bills as "Most Popular" for unauthenticated visitors
- FollowButton opens AuthModal instead of acting when not signed in
- Members page pins followed members at the top for quick unfollowing
- useFollows skips API call and invalidates dashboard on follow/unfollow

Authored-By: Jack Levy
2026-03-01 15:54:54 -05:00
Jack Levy
73881b2404 feat(notifications): follow modes, milestone alerts, notification enhancements
Follow Modes (neutral / pocket_veto / pocket_boost):
- Alembic migration 0013 adds follow_mode column to follows table
- FollowButton rewritten as mode-aware dropdown for bills; simple toggle for members/topics
- PATCH /api/follows/{id}/mode endpoint with validation
- Dispatcher filters pocket_veto follows (suppress new_document/new_amendment events)
- Dispatcher adds ntfy Actions header for pocket_boost follows

Change-driven (milestone) Alerts:
- New notification_utils.py with shared emit helpers and 30-min dedup
- congress_poller emits bill_updated events on milestone action text
- llm_processor replaced with shared emit util (also notifies member/topic followers)

Notification Enhancements:
- ntfy priority levels (high for bill_updated, default for others)
- Quiet hours (UTC): dispatcher holds events outside allowed window
- Digest mode (daily/weekly): send_notification_digest Celery beat task
- Notification history endpoint + Recent Alerts UI section
- Enriched following page (bill titles, member photos/details via sub-components)
- Follow mode test buttons in admin settings panel

Infrastructure:
- nginx: switch upstream blocks to set $variable proxy_pass so Docker DNS
  re-resolves upstream IPs after container rebuilds (valid=10s)
- TROUBLESHOOTING.md documenting common Docker/nginx/postgres gotchas

Authored-By: Jack Levy
2026-03-01 15:09:13 -05:00
Jack Levy
2e2fefb795 feat: per-user notifications (ntfy + RSS), deduplicated actions, backfill task
Notifications:
- New /notifications page accessible to all users (ntfy + RSS config)
- ntfy now supports no-auth, Bearer token, and HTTP Basic auth (for ACL-protected self-hosted servers)
- RSS enabled/disabled independently of ntfy; token auto-generated on first GET
- Notification settings removed from admin-only Settings page; replaced with link card
- Sidebar adds Notifications nav link for all users
- notification_dispatcher.py: fan-out now marks RSS events dispatched independently

Action history:
- Migration 0012: deduplicates existing bill_actions rows and adds UNIQUE(bill_id, action_date, action_text)
- congress_poller.py: replaces existence-check inserts with ON CONFLICT DO NOTHING (race-condition safe)
- Added backfill_all_bill_actions task (no date filter) + admin endpoint POST /backfill-all-actions

Authored-By: Jack Levy
2026-03-01 12:04:13 -05:00
Jack Levy
f3a8c1218a Add chamber color badges, action history fallback, and task status polling
- Add chamberBadgeColor util: amber/gold for Senate, slate/silver for House
- Apply chamber badge to BillCard and bill detail header
- ActionTimeline: show latest_action_date/text as fallback when full history
  not yet fetched, with note that full history loads in background
- Manual Controls: poll task status every 5s after triggering, show spinning
  indicator while running, task ID prefix, and completion/failure state

Authored-By: Jack Levy
2026-03-01 11:29:11 -05:00
Jack Levy
37339d6950 feat(ui): add mobile-responsive layout with hamburger drawer
Adds MobileHeader with hamburger button (left-aligned) that opens a
slide-in sidebar drawer on mobile. Desktop layout is unchanged. All
hardcoded multi-column grids updated with responsive Tailwind breakpoints.

Co-Authored-By: Jack Levy
2026-02-28 23:58:28 -05:00
Jack Levy
5b73b60d9e Add multi-user auth system and admin panel
- User model with email/hashed_password/is_admin/notification_prefs
- JWT auth: POST /api/auth/register, /login, /me
- First registered user auto-promoted to admin
- Migration 0005: users table + user_id FK on follows (clears global follows)
- Follows, dashboard, settings, admin endpoints all require authentication
- Admin endpoints (settings writes, celery triggers) require is_admin
- Frontend: login/register pages, Zustand auth store (localStorage persist)
- AuthGuard component gates all app routes, shows app shell only when authed
- Sidebar shows user email + logout; Admin nav link visible to admins only
- Admin panel (/settings): user list with delete + promote/demote, LLM config,
  data source settings, and manual celery controls

Authored-By: Jack Levy
2026-02-28 21:44:34 -05:00
Jack Levy
e418dd9ae0 Initial commit 2026-02-28 21:08:19 -05:00