diff options
| author | schererleander <leander@schererleander.de> | 2025-12-26 16:07:58 +0100 |
|---|---|---|
| committer | schererleander <leander@schererleander.de> | 2025-12-26 16:07:58 +0100 |
| commit | ba8c98a8dccb8b561747168b90ae769a105d37cf (patch) | |
| tree | f67f1b23510d27a16c6ac0bfc449a6aece6c81a2 /src/components | |
| parent | 4b564ae2ef6d47e548ff3f1c6513605b8f2509ed (diff) | |
refactor: implement server-side rendering for auth-dependent pages
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Navbar.tsx | 83 | ||||
| -rw-r--r-- | src/components/UserNav.tsx | 82 |
2 files changed, 91 insertions, 74 deletions
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index f75e6ed..e8f4138 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,31 +1,14 @@ -"use client" - import Link from "next/link" -import { useSession, signOut } from "next-auth/react" +import { getServerSession } from "next-auth" +import { LogIn } from "lucide-react" + import { Button } from "@/components/ui/button" -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { LogIn, Settings, LogOut } from "lucide-react" import { ThemeToggle } from "@/components/ThemeToggle" +import { UserNav } from "@/components/UserNav" +import { authOptions } from "@/lib/auth" -export default function Navbar() { - const { data: session, status } = useSession() - - const getInitials = (name: string) => { - return name - .split(' ') - .map(word => word[0]) - .join('') - .toUpperCase() - .slice(0, 2) - } +export default async function Navbar() { + const session = await getServerSession(authOptions) return ( <nav className="border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"> @@ -39,56 +22,8 @@ export default function Navbar() { <div className="flex items-center space-x-4"> <ThemeToggle /> - {status === "loading" ? ( - <div className="flex items-center space-x-2"> - <div className="w-16 h-4 bg-muted animate-pulse rounded hidden sm:block" /> - <div className="w-8 h-8 rounded-full bg-muted animate-pulse" /> - </div> - ) : session ? ( - <DropdownMenu> - <DropdownMenuTrigger asChild> - <Button variant="ghost" className="relative h-10 w-auto px-2 rounded-full"> - <div className="flex items-center space-x-2"> - <span className="text-sm font-medium hidden sm:block"> - {session.user?.name} - </span> - <Avatar className="h-8 w-8"> - <AvatarImage src={session.user?.image || ""} alt={session.user?.name || ""} /> - <AvatarFallback className="text-xs"> - {session.user?.name ? getInitials(session.user.name) : "U"} - </AvatarFallback> - </Avatar> - </div> - </Button> - </DropdownMenuTrigger> - <DropdownMenuContent className="w-56" align="end" forceMount> - <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="/settings" className="w-full cursor-pointer"> - <Settings className="mr-2 h-4 w-4" /> - Settings - </Link> - </DropdownMenuItem> - <DropdownMenuSeparator /> - <DropdownMenuItem - className="cursor-pointer text-destructive hover:text-destructive focus:text-destructive" - onClick={() => signOut()} - > - <LogOut className="mr-2 h-4 w-4 text-destructive" /> - Log out - </DropdownMenuItem> - </DropdownMenuContent> - </DropdownMenu> + {session?.user ? ( + <UserNav user={session.user} /> ) : ( <Button asChild> <Link href="/login"> diff --git a/src/components/UserNav.tsx b/src/components/UserNav.tsx new file mode 100644 index 0000000..e11cad7 --- /dev/null +++ b/src/components/UserNav.tsx @@ -0,0 +1,82 @@ +"use client" + +import Link from "next/link" +import { signOut } from "next-auth/react" +import { LogOut, Settings } from "lucide-react" + +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +interface UserNavProps { + user: { + name?: string | null + email?: string | null + image?: string | null + } +} + +export function UserNav({ user }: UserNavProps) { + const getInitials = (name: string) => { + return name + .split(' ') + .map(word => word[0]) + .join('') + .toUpperCase() + .slice(0, 2) + } + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="ghost" className="relative h-10 w-auto px-2 rounded-full"> + <div className="flex items-center space-x-2"> + <span className="text-sm font-medium hidden sm:block"> + {user.name} + </span> + <Avatar className="h-8 w-8"> + <AvatarImage src={user.image || ""} alt={user.name || ""} /> + <AvatarFallback className="text-xs"> + {user.name ? getInitials(user.name) : "U"} + </AvatarFallback> + </Avatar> + </div> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent className="w-56" align="end" forceMount> + <DropdownMenuLabel className="font-normal"> + <div className="flex flex-col space-y-1"> + <p className="text-sm font-medium leading-none"> + {user.name} + </p> + <p className="text-xs leading-none text-muted-foreground"> + {user.email} + </p> + </div> + </DropdownMenuLabel> + <DropdownMenuSeparator /> + <DropdownMenuItem asChild> + <Link href="/settings" className="w-full cursor-pointer"> + <Settings className="mr-2 h-4 w-4" /> + Settings + </Link> + </DropdownMenuItem> + <DropdownMenuSeparator /> + <DropdownMenuItem + className="cursor-pointer text-destructive hover:text-destructive focus:text-destructive" + onClick={() => signOut()} + > + <LogOut className="mr-2 h-4 w-4 text-destructive" /> + Log out + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ) +} |
