feat: add user profile page and replace nav welcome text with avatar dropdown

- Replace welcome text in navigation with clickable avatar dropdown
- Add dropdown menu showing user name, email, profile link, and logout
- Create comprehensive user profile page (/profile) with:
  - Profile overview with avatar, name, email, and verification status
  - Account information section with detailed user data
  - Account activity tracking with current session status
  - Quick actions section with placeholder buttons for future features
- Add missing UI components (Card, Badge, Separator) following shadcn/ui patterns
- Use proper design tokens and destructive variant for logout action
- All components pass lint and type checking

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Leon van Zyl
2025-08-27 08:12:09 +02:00
parent 9377f6eabb
commit eb71ad75b6
5 changed files with 422 additions and 22 deletions

View File

@@ -1,12 +1,23 @@
"use client";
import { useSession } from "@/lib/auth-client";
import { useSession, signOut } from "@/lib/auth-client";
import { SignInButton } from "./sign-in-button";
import { SignOutButton } from "./sign-out-button";
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { User, LogOut } from "lucide-react";
export function UserProfile() {
const { data: session, isPending } = useSession();
const router = useRouter();
if (isPending) {
return <div>Loading...</div>;
@@ -20,26 +31,54 @@ export function UserProfile() {
);
}
const handleSignOut = async () => {
await signOut();
router.replace("/");
router.refresh();
};
return (
<div className="flex items-center gap-3">
<span className="text-sm text-muted-foreground">
Welcome {session.user?.name}
</span>
<Avatar className="size-8">
<AvatarImage
src={session.user?.image || ""}
alt={session.user?.name || "User"}
referrerPolicy="no-referrer"
/>
<AvatarFallback>
{(
session.user?.name?.[0] ||
session.user?.email?.[0] ||
"U"
).toUpperCase()}
</AvatarFallback>
</Avatar>
<SignOutButton />
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Avatar className="size-8 cursor-pointer hover:opacity-80 transition-opacity">
<AvatarImage
src={session.user?.image || ""}
alt={session.user?.name || "User"}
referrerPolicy="no-referrer"
/>
<AvatarFallback>
{(
session.user?.name?.[0] ||
session.user?.email?.[0] ||
"U"
).toUpperCase()}
</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">
{session.user?.name}
</p>
<p className="text-xs leading-none text-muted-foreground">
{session.user?.email}
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/profile" className="flex items-center">
<User className="mr-2 h-4 w-4" />
Your Profile
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleSignOut} variant="destructive">
<LogOut className="mr-2 h-4 w-4" />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}