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
This commit is contained in:
@@ -20,13 +20,13 @@ import { ThemeToggle } from "./ThemeToggle";
|
||||
import { useAuthStore } from "@/stores/authStore";
|
||||
|
||||
const NAV = [
|
||||
{ href: "/", label: "Dashboard", icon: LayoutDashboard, adminOnly: false },
|
||||
{ href: "/bills", label: "Bills", icon: FileText, adminOnly: false },
|
||||
{ href: "/members", label: "Members", icon: Users, adminOnly: false },
|
||||
{ href: "/topics", label: "Topics", icon: Tags, adminOnly: false },
|
||||
{ href: "/following", label: "Following", icon: Heart, adminOnly: false },
|
||||
{ href: "/notifications", label: "Notifications", icon: Bell, adminOnly: false },
|
||||
{ href: "/settings", label: "Admin", icon: Settings, adminOnly: true },
|
||||
{ href: "/", label: "Dashboard", icon: LayoutDashboard, adminOnly: false, requiresAuth: false },
|
||||
{ href: "/bills", label: "Bills", icon: FileText, adminOnly: false, requiresAuth: false },
|
||||
{ href: "/members", label: "Members", icon: Users, adminOnly: false, requiresAuth: false },
|
||||
{ href: "/topics", label: "Topics", icon: Tags, adminOnly: false, requiresAuth: false },
|
||||
{ href: "/following", label: "Following", icon: Heart, adminOnly: false, requiresAuth: true },
|
||||
{ href: "/notifications", label: "Notifications", icon: Bell, adminOnly: false, requiresAuth: true },
|
||||
{ href: "/settings", label: "Admin", icon: Settings, adminOnly: true, requiresAuth: false },
|
||||
];
|
||||
|
||||
export function Sidebar({ onClose }: { onClose?: () => void }) {
|
||||
@@ -34,6 +34,7 @@ export function Sidebar({ onClose }: { onClose?: () => void }) {
|
||||
const router = useRouter();
|
||||
const qc = useQueryClient();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const token = useAuthStore((s) => s.token);
|
||||
const logout = useAuthStore((s) => s.logout);
|
||||
|
||||
function handleLogout() {
|
||||
@@ -55,7 +56,11 @@ export function Sidebar({ onClose }: { onClose?: () => void }) {
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 p-3 space-y-1">
|
||||
{NAV.filter(({ adminOnly }) => !adminOnly || user?.is_admin).map(({ href, label, icon: Icon }) => {
|
||||
{NAV.filter(({ adminOnly, requiresAuth }) => {
|
||||
if (adminOnly && !user?.is_admin) return false;
|
||||
if (requiresAuth && !token) return false;
|
||||
return true;
|
||||
}).map(({ href, label, icon: Icon }) => {
|
||||
const active = href === "/" ? pathname === "/" : pathname.startsWith(href);
|
||||
return (
|
||||
<Link
|
||||
@@ -77,18 +82,31 @@ export function Sidebar({ onClose }: { onClose?: () => void }) {
|
||||
</nav>
|
||||
|
||||
<div className="p-3 border-t border-border space-y-2">
|
||||
{user && (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-muted-foreground truncate max-w-[120px]" title={user.email}>
|
||||
{user.email}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="p-1 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent"
|
||||
title="Sign out"
|
||||
>
|
||||
<LogOut className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
{token ? (
|
||||
<>
|
||||
{user && (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-muted-foreground truncate max-w-[120px]" title={user.email}>
|
||||
{user.email}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="p-1 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent"
|
||||
title="Sign out"
|
||||
>
|
||||
<LogOut className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Link href="/register" onClick={onClose} className="w-full px-3 py-1.5 text-sm font-medium text-center rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors">
|
||||
Register
|
||||
</Link>
|
||||
<Link href="/login" onClick={onClose} className="w-full px-3 py-1.5 text-sm font-medium text-center rounded-md border border-border text-foreground hover:bg-accent transition-colors">
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user