From a0e7ab4cd38a72de7ccef79b8fafd16d5271baba Mon Sep 17 00:00:00 2001 From: Jack Levy Date: Sun, 1 Mar 2026 21:22:16 -0500 Subject: [PATCH] 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 --- ARCHITECTURE.md | 17 ++++ MVP threshold this make v1 complete.md | 93 ++++++++++++++++++++ PocketVeto — Feature Roadmap.md | 6 +- frontend/app/page.tsx | 3 + frontend/components/shared/WelcomeBanner.tsx | 71 +++++++++++++++ frontend/lib/hooks/useDashboard.ts | 4 +- 6 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 MVP threshold this make v1 complete.md create mode 100644 frontend/components/shared/WelcomeBanner.tsx diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c84d3f0..5f730d3 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -764,6 +764,9 @@ Compact bill preview showing bill ID, title, sponsor with party badge, latest ac **`TrendChart.tsx`** Line chart of `composite_score` over time with tooltip breakdown of each data source. +**`WelcomeBanner.tsx`** +Dismissible onboarding card rendered at the top of the dashboard. Shown only to guests (no JWT token). On dismiss — via the × button, the "Dismiss" link, or the "Browse Bills" CTA — sets `localStorage["pv_seen_welcome"] = "1"` and hides permanently. Reads localStorage after mount to avoid hydration mismatch; renders nothing until client-side state is resolved. + ### Utility Functions (`lib/utils.ts`) ```typescript @@ -794,6 +797,8 @@ interface AuthState { // Persisted to localStorage as "pocketveto-auth" ``` +**Auth-aware query keys:** TanStack Query keys that return different data for guests vs authenticated users include `!!token` in their key (e.g. `["dashboard", !!token]`). This ensures a fresh fetch fires automatically on login or logout without manual cache invalidation. + --- ## Authentication @@ -993,6 +998,18 @@ Nginx uses `resolver 127.0.0.11 valid=10s` (Docker's internal DNS) so upstream c - Manual Controls split into two sections: always-visible recurring controls (Poll, Members, Trends, Actions, Resume) and a collapsible **Maintenance** section for one-time backfill tasks - Maintenance section header shows "⚠ action needed" when any backfill has a non-zero count +### v0.6.1 — Welcome Banner & Dashboard Auth Fix + +**Welcome Banner:** +- `WelcomeBanner.tsx` — dismissible onboarding card shown only to guests at the top of the dashboard +- Three bullet points: follow bills/members/topics, see what changed, Back to Source citations +- "Browse Bills" CTA navigates to `/bills` and dismisses; × and "Dismiss" button also dismiss +- Dismissed state stored in `localStorage["pv_seen_welcome"]`; never shown to logged-in users + +**Dashboard Auth-Aware Query Key:** +- `useDashboard` hook query key changed from `["dashboard"]` to `["dashboard", !!token]` +- Fixes stale cache issue where logging in showed the guest feed until a manual refresh + ### v0.2.2 — Sponsor Linking & Search Fixes - **Root cause fixed:** Congress.gov list API does not return sponsor data — only the detail endpoint does. Poller now calls the detail endpoint for each new bill to get the sponsor and populate `bill.sponsor_id` - **Backfill task:** `backfill_sponsor_ids` Celery task + `/api/admin/backfill-sponsors` endpoint + "Backfill Sponsors" button in Admin UI — fixes existing bills with `NULL` sponsor_id (~10 req/sec, ~3 min for 1,600 bills) diff --git a/MVP threshold this make v1 complete.md b/MVP threshold this make v1 complete.md new file mode 100644 index 0000000..934bc4b --- /dev/null +++ b/MVP threshold this make v1 complete.md @@ -0,0 +1,93 @@ + +### Phase 3 — Personal Workflow & Context + +_Focus: Allowing users to organize data and adding the "Cross-Bill" intelligence layer._ + +- **Collections / Watchlists:** `collections` table for public/private bill groupings shareable via UUID tokens. + +- **Personal Notes:** Private `bill_notes` table to store user stances, tags, and pinned insights on bill detail pages. + + +- **Cross-Bill Referencing (New):** + + - **Extraction:** Update LLM prompts to identify and extract related bill identifiers (e.g., "H.R. 1234") from text. + + - **Referential Table:** Implement `bill_references` to map source-to-target bill relationships with context quotes. + + - **Ghost Hydration:** Create the `/api/admin/bills/hydrate` endpoint to fetch and analyze referenced bills not currently in the DB. + + - **UI Hover Previews:** Use `Radix UI` popovers in the `BriefPanel` to show snapshots of referenced legislation without leaving the page. + +- **Weekly Digest:** Celery beat task to dispatch 7-day summaries of followed bills via ntfy and RSS. + + +--- + +### Phase 4 — Accountability & Member Scoring + +_Focus: Deep-diving into legislative performance and voting records._ + +- **Votes & Committees:** Fetch roll-call votes and committee actions from Congress.gov into a new `bill_votes` table. + +- **Member Effectiveness Score:** Nightly Celery task calculating scores based on sponsored bills, advancement milestones, and "bills enacted" metrics. + +- **Bipartisan Progress Metric (Proposed):** Enhance effectiveness scores by weighting co-sponsorship across party lines and committee movement in opposing chambers. + +- **Representation Alignment:** Neutral view showing how member votes align with a user’s followed topics. + + +--- + +### Phase 5 — Systemic Intelligence + +_Focus: Enhancing search and providing broader legislative context._ + +- **Search Improvements:** Add filters for bill type, status, chamber, and date; implement "Search within member bills". + +- **Topic-Scoped Search:** Allow users to search legislation exclusively within followed topics like "Healthcare" or "Energy". + +- **Member Effectiveness Transparency:** Display the "Member Effectiveness" formula on profile pages to maintain non-partisan trust. + + +--- + +### Phase 6 — Desktop & Polish + +_Focus: Optimization for professional-grade power users._ + +- **Desktop View:** Multi-column layout with sticky sidebars and expanded grids optimized for large screens. + +- **Notification Channels v2:** Expand beyond ntfy/RSS to include Discord webhooks, Telegram, and SMTP email. + +- **Backfill Tasks:** Automate `first_name` and `last_name` population for all member records. + + +--- + +## 🛠️ Updated Technical Implementation Details + +### Database Schema Updates + +|**Table**|**New Columns / Purpose**| +|---|---| +|**`bill_references`**|`source_id`, `target_id`, `context_quote` to link related bills.| +|**`bill_notes`**|`user_id`, `bill_id`, `content`, `stance`, `tags`, `pinned`.| +|**`collections`**|`user_id`, `name`, `slug`, `is_public` for shared watchlists.| +|**`bill_votes`**|Stores roll-call data and results for accountability tracking.| + +### Enhanced LLM Pipeline + +The `llm_processor.py` task will now perform a dual-pass: + +1. **Analysis:** Generate the standard brief with citations and fact/inference labels. + +2. **Mapping:** Extract bill IDs found in the text and insert them into the `bill_references` table. If a target ID is missing from the `bills` table, it triggers a low-priority `hydrate_bill` task. + + +### Performance Guardrails + +- **Selective Hydration:** The poller will only "Ghost Hydrate" referenced bills if they are from the current or previous Congress to prevent API exhaustion. + +- **Cached Previews:** Referenced bill snapshots for hover tooltips will be cached in Redis to minimize database hits during dashboard scrolling. + + diff --git a/PocketVeto — Feature Roadmap.md b/PocketVeto — Feature Roadmap.md index 2425b5d..50d43a7 100644 --- a/PocketVeto — Feature Roadmap.md +++ b/PocketVeto — Feature Roadmap.md @@ -206,9 +206,9 @@ Don’t store it server-side unless you already have user accounts and it’s pa ### Backlog item (checkboxes) -* [ ] First-visit welcome UI (banner/card + optional toast) -* [ ] Dismiss + “don’t show again” (localStorage) -* [ ] CTA: Add first follow +* [x] First-visit welcome UI (banner/card — guests only, shown on dashboard) +* [x] Dismiss + “don’t show again” (localStorage `pv_seen_welcome`) +* [x] CTA: Browse Bills * [ ] CTA: Load demo data (optional) * [ ] Link: “How it works” page/modal (optional) diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index e880902..ebc21a1 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -4,6 +4,7 @@ import { TrendingUp, BookOpen, Flame, RefreshCw } from "lucide-react"; import Link from "next/link"; import { useDashboard } from "@/lib/hooks/useDashboard"; import { BillCard } from "@/components/shared/BillCard"; +import { WelcomeBanner } from "@/components/shared/WelcomeBanner"; import { adminAPI } from "@/lib/api"; import { useState } from "react"; import { useAuthStore } from "@/stores/authStore"; @@ -40,6 +41,8 @@ export default function DashboardPage() { + + {isLoading ? (
Loading dashboard...
) : ( diff --git a/frontend/components/shared/WelcomeBanner.tsx b/frontend/components/shared/WelcomeBanner.tsx new file mode 100644 index 0000000..98a8020 --- /dev/null +++ b/frontend/components/shared/WelcomeBanner.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { X, BookOpen, GitCompare, ShieldCheck } from "lucide-react"; +import Link from "next/link"; +import { useAuthStore } from "@/stores/authStore"; + +const STORAGE_KEY = "pv_seen_welcome"; + +export function WelcomeBanner() { + const token = useAuthStore((s) => s.token); + const [visible, setVisible] = useState(false); + + useEffect(() => { + if (!token && localStorage.getItem(STORAGE_KEY) !== "1") { + setVisible(true); + } + }, [token]); + + const dismiss = () => { + localStorage.setItem(STORAGE_KEY, "1"); + setVisible(false); + }; + + if (!visible) return null; + + return ( +
+ + +

Welcome to PocketVeto

+ +
    +
  • + + Follow bills, members, or topics — get low-noise alerts when things actually move +
  • +
  • + + See what changed in plain English when bills are amended +
  • +
  • + + Verify every AI claim with Back to Source citations from the bill text +
  • +
+ +
+ + Browse Bills + + +
+
+ ); +} diff --git a/frontend/lib/hooks/useDashboard.ts b/frontend/lib/hooks/useDashboard.ts index a7174ce..57870ee 100644 --- a/frontend/lib/hooks/useDashboard.ts +++ b/frontend/lib/hooks/useDashboard.ts @@ -1,9 +1,11 @@ import { useQuery } from "@tanstack/react-query"; import { dashboardAPI } from "../api"; +import { useAuthStore } from "@/stores/authStore"; export function useDashboard() { + const token = useAuthStore((s) => s.token); return useQuery({ - queryKey: ["dashboard"], + queryKey: ["dashboard", !!token], queryFn: () => dashboardAPI.get(), staleTime: 2 * 60 * 1000, refetchInterval: 5 * 60 * 1000,