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
This commit is contained in:
@@ -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() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<WelcomeBanner />
|
||||
|
||||
{isLoading ? (
|
||||
<div className="text-center py-20 text-muted-foreground">Loading dashboard...</div>
|
||||
) : (
|
||||
|
||||
71
frontend/components/shared/WelcomeBanner.tsx
Normal file
71
frontend/components/shared/WelcomeBanner.tsx
Normal file
@@ -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 (
|
||||
<div className="relative bg-card border border-border rounded-lg p-5 pr-10">
|
||||
<button
|
||||
onClick={dismiss}
|
||||
title="Dismiss"
|
||||
className="absolute top-3 right-3 p-1 rounded text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
|
||||
<h2 className="font-semibold text-base mb-3">Welcome to PocketVeto</h2>
|
||||
|
||||
<ul className="space-y-2 mb-4">
|
||||
<li className="flex items-start gap-2.5 text-sm text-muted-foreground">
|
||||
<BookOpen className="w-4 h-4 mt-0.5 shrink-0 text-primary" />
|
||||
Follow bills, members, or topics — get low-noise alerts when things actually move
|
||||
</li>
|
||||
<li className="flex items-start gap-2.5 text-sm text-muted-foreground">
|
||||
<GitCompare className="w-4 h-4 mt-0.5 shrink-0 text-primary" />
|
||||
See <em>what changed</em> in plain English when bills are amended
|
||||
</li>
|
||||
<li className="flex items-start gap-2.5 text-sm text-muted-foreground">
|
||||
<ShieldCheck className="w-4 h-4 mt-0.5 shrink-0 text-primary" />
|
||||
Verify every AI claim with <strong>Back to Source</strong> citations from the bill text
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Link
|
||||
href="/bills"
|
||||
onClick={dismiss}
|
||||
className="px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
Browse Bills
|
||||
</Link>
|
||||
<button
|
||||
onClick={dismiss}
|
||||
className="px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user