Add email as a second notification channel alongside ntfy:
- Tabbed channel selector: ntfy | Email | Telegram (coming soon) | Discord (coming soon)
- Active channel shown with green status dot on tab
- Email tab: address input, Save & Enable, Test, Disable — same UX pattern as ntfy
- Backend: SMTP config in settings (SMTP_HOST/PORT/USER/PASSWORD/FROM/STARTTLS)
- Dispatcher: _send_email() helper wired into dispatch_notifications
- POST /api/notifications/test/email endpoint with descriptive error messages
- Email fires in same window as ntfy (respects quiet hours / digest hold)
- Telegram and Discord tabs show coming-soon banners with planned feature description
- .env.example documents all SMTP settings with provider examples
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
- 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