header.tsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. "use client";
  2. import Link from "next/link";
  3. import Image from "next/image";
  4. import { LoginLink, LogoutLink, RegisterLink } from "@kinde-oss/kinde-auth-nextjs/server";
  5. import { useKindeBrowserClient } from "@kinde-oss/kinde-auth-nextjs";
  6. import { useTheme } from "@/app/providers";
  7. import {
  8. NavigationMenu,
  9. NavigationMenuContent,
  10. NavigationMenuItem,
  11. NavigationMenuLink,
  12. NavigationMenuList,
  13. NavigationMenuTrigger,
  14. } from "@/components/ui/navigation-menu";
  15. import { Button } from "@/components/ui/button";
  16. import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
  17. import { Menu, Home, Settings, User, LogOut, LogIn, ArrowLeft } from "lucide-react";
  18. import { DarkModeToggle } from "./dark-mode-toggle";
  19. import React from "react";
  20. import { usePathname } from "next/navigation";
  21. const userMenuItems = [
  22. {
  23. title: "Profile",
  24. href: "/profile",
  25. description: "Manage your profile settings",
  26. icon: User,
  27. },
  28. {
  29. title: "Settings",
  30. href: "/settings",
  31. description: "Configure your preferences",
  32. icon: Settings,
  33. },
  34. ];
  35. export default function Header() {
  36. const { isAuthenticated, getUser, isLoading } = useKindeBrowserClient();
  37. const { theme } = useTheme();
  38. const user = getUser();
  39. const logoSrc = theme === "dark" ? "/vtorio-dark.svg" : "/vtorio.svg";
  40. const pathname = usePathname();
  41. // Don't show on dashboard or home page
  42. const shouldShowBackButton = pathname !== '/dashboard' && pathname !== '/';
  43. if (isLoading) {
  44. return (
  45. <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
  46. <div className="flex h-16 items-center justify-between">
  47. <div className="flex items-center gap-6 pl-4 md:pl-6">
  48. <Link href="/" className="flex items-center space-x-2">
  49. <Image
  50. src={logoSrc}
  51. alt="vtor.io"
  52. width={32}
  53. height={8}
  54. className="h-8 w-auto"
  55. priority
  56. />
  57. </Link>
  58. {shouldShowBackButton && (
  59. <Link href="/dashboard" className="flex items-center space-x-2 text-sm font-medium hover:text-accent-foreground transition-colors">
  60. <ArrowLeft className="h-4 w-4" />
  61. <span>Back to dashboard</span>
  62. </Link>
  63. )}
  64. </div>
  65. <div className="flex items-center gap-4 pr-4 md:pr-6">
  66. <div className="h-8 w-20 bg-gray-200 animate-pulse rounded"></div>
  67. </div>
  68. </div>
  69. </header>
  70. );
  71. }
  72. return (
  73. <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
  74. <div className="flex h-16 items-center justify-between pr-6">
  75. <div className="flex items-center gap-6 pl-4 md:pl-6">
  76. <Link href="/" className="flex items-center space-x-2">
  77. <Image
  78. src={logoSrc}
  79. alt="vtor.io"
  80. width={32}
  81. height={8}
  82. className="h-8 w-auto"
  83. priority
  84. />
  85. </Link>
  86. {shouldShowBackButton && (
  87. <Link href="/dashboard">
  88. <Button variant="outline" size="sm" className="flex items-center space-x-2">
  89. <ArrowLeft className="h-4 w-4" />
  90. <span>Back to dashboard</span>
  91. </Button>
  92. </Link>
  93. )}
  94. </div>
  95. <div className="flex items-center gap-4 pr-0 md:pr-0">
  96. {isAuthenticated ? (
  97. <>
  98. <NavigationMenu className="hidden md:flex">
  99. <NavigationMenuList>
  100. <NavigationMenuItem>
  101. <NavigationMenuTrigger className="h-9">
  102. <div className="flex items-center gap-2">
  103. <Image
  104. className="rounded-full"
  105. src={user?.picture || "/default-avatar.png"}
  106. alt="User Avatar"
  107. width={24}
  108. height={24}
  109. />
  110. <span className="text-sm font-medium">{user?.given_name || user?.email}</span>
  111. </div>
  112. </NavigationMenuTrigger>
  113. <NavigationMenuContent>
  114. <ul className="grid gap-3 p-6 w-[300px]">
  115. {userMenuItems.map((item) => (
  116. <li key={item.title}>
  117. <NavigationMenuLink asChild>
  118. <Link
  119. href={item.href}
  120. className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground">
  121. <div className="flex items-center gap-2">
  122. <item.icon className="h-4 w-4" />
  123. <div className="text-sm font-medium leading-none">{item.title}</div>
  124. </div>
  125. <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
  126. {item.description}
  127. </p>
  128. </Link>
  129. </NavigationMenuLink>
  130. </li>
  131. ))}
  132. <li>
  133. <LogoutLink className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground">
  134. <div className="flex items-center gap-2">
  135. <LogOut className="h-4 w-4" />
  136. <div className="text-sm font-medium leading-none">Logout</div>
  137. </div>
  138. <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
  139. Sign out of your account
  140. </p>
  141. </LogoutLink>
  142. </li>
  143. </ul>
  144. </NavigationMenuContent>
  145. </NavigationMenuItem>
  146. </NavigationMenuList>
  147. </NavigationMenu>
  148. </>
  149. ) : (
  150. <div className="hidden md:flex items-center gap-2">
  151. <LoginLink>
  152. <Button variant="ghost" size="sm">
  153. <LogIn className="h-4 w-4 mr-2" />
  154. Login
  155. </Button>
  156. </LoginLink>
  157. </div>
  158. )}
  159. <DarkModeToggle />
  160. {/* Mobile Menu */}
  161. <Sheet>
  162. <SheetTrigger asChild className="md:hidden">
  163. <Button variant="ghost" size="icon">
  164. <Menu className="h-5 w-5" />
  165. <span className="sr-only">Toggle menu</span>
  166. </Button>
  167. </SheetTrigger>
  168. <SheetContent side="right" className="w-[300px]">
  169. <div className="flex flex-col space-y-4 mt-8">
  170. <Link href="/" className="flex items-center space-x-2">
  171. <Home className="h-4 w-4" />
  172. <span>Home</span>
  173. </Link>
  174. {isAuthenticated ? (
  175. <>
  176. <div className="border-t pt-4">
  177. <div className="flex items-center space-x-2 mb-4">
  178. <Image
  179. className="rounded-full"
  180. src={user?.picture || "/default-avatar.png"}
  181. alt="User Avatar"
  182. width={32}
  183. height={32}
  184. />
  185. <span className="font-medium">{user?.given_name || user?.email}</span>
  186. </div>
  187. {userMenuItems.map((item) => (
  188. <Link
  189. key={item.title}
  190. href={item.href}
  191. className="flex items-center space-x-2 py-2">
  192. <item.icon className="h-4 w-4" />
  193. <span>{item.title}</span>
  194. </Link>
  195. ))}
  196. <LogoutLink className="flex items-center space-x-2 py-2 text-red-600">
  197. <LogOut className="h-4 w-4" />
  198. <span>Logout</span>
  199. </LogoutLink>
  200. </div>
  201. </>
  202. ) : (
  203. <div className="border-t pt-4 space-y-2">
  204. <LoginLink>
  205. <Button variant="ghost" className="w-full justify-start">
  206. <LogIn className="h-4 w-4 mr-2" />
  207. Login
  208. </Button>
  209. </LoginLink>
  210. <RegisterLink>
  211. <Button className="w-full">Sign Up</Button>
  212. </RegisterLink>
  213. </div>
  214. )}
  215. </div>
  216. </SheetContent>
  217. </Sheet>
  218. </div>
  219. </div>
  220. </header>
  221. );
  222. }