header.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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, FileText, Home, Folder, Settings, User, LogOut, LogIn } from "lucide-react";
  18. import { DarkModeToggle } from "./dark-mode-toggle";
  19. import { cn } from "@/lib/utils";
  20. import React from "react";
  21. const navigationItems = [
  22. {
  23. title: "Files",
  24. href: "/files",
  25. description: "Browse and manage your files",
  26. icon: Folder,
  27. },
  28. {
  29. title: "Layout Configurations",
  30. href: "/layout-configurations",
  31. description: "Manage Excel layout configurations",
  32. icon: FileText,
  33. },
  34. {
  35. title: "Imports",
  36. href: "/imports",
  37. description: "Manage data imports",
  38. icon: FileText,
  39. },
  40. {
  41. title: "API Docs",
  42. href: "/api-docs",
  43. description: "View API documentation",
  44. icon: FileText,
  45. },
  46. ];
  47. const userMenuItems = [
  48. {
  49. title: "Profile",
  50. href: "/profile",
  51. description: "Manage your profile settings",
  52. icon: User,
  53. },
  54. {
  55. title: "Settings",
  56. href: "/settings",
  57. description: "Configure your preferences",
  58. icon: Settings,
  59. },
  60. ];
  61. export default function Header() {
  62. const { isAuthenticated, getUser, isLoading } = useKindeBrowserClient();
  63. const { theme } = useTheme();
  64. const user = getUser();
  65. const logoSrc = theme === "dark" ? "/vtorio-dark.svg" : "/vtorio.svg";
  66. if (isLoading) {
  67. return (
  68. <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
  69. <div className="container flex h-16 items-center justify-between">
  70. <div className="flex items-center gap-6">
  71. <Link href="/" className="flex items-center space-x-2">
  72. <Image
  73. src={logoSrc}
  74. alt="vtor.io"
  75. width={32}
  76. height={8}
  77. className="h-8 w-auto"
  78. priority
  79. />
  80. </Link>
  81. </div>
  82. <div className="flex items-center gap-4">
  83. <div className="h-8 w-20 bg-gray-200 animate-pulse rounded"></div>
  84. </div>
  85. </div>
  86. </header>
  87. );
  88. }
  89. return (
  90. <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
  91. <div className="container flex h-16 items-center justify-between">
  92. <div className="flex items-center gap-6">
  93. <Link href="/" className="flex items-center space-x-2">
  94. <Image
  95. src={logoSrc}
  96. alt="vtor.io"
  97. width={32}
  98. height={8}
  99. className="h-8 w-auto"
  100. priority
  101. />
  102. </Link>
  103. <NavigationMenu className="hidden md:flex">
  104. <NavigationMenuList>
  105. {navigationItems.map((item) => (
  106. <NavigationMenuItem key={item.title}>
  107. <NavigationMenuLink
  108. href={item.href}
  109. className={cn(
  110. "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50"
  111. )}>
  112. <item.icon className="mr-2 h-4 w-4" />
  113. {item.title}
  114. </NavigationMenuLink>
  115. </NavigationMenuItem>
  116. ))}
  117. </NavigationMenuList>
  118. </NavigationMenu>
  119. </div>
  120. <div className="flex items-center gap-4">
  121. <DarkModeToggle />
  122. {isAuthenticated ? (
  123. <>
  124. <NavigationMenu className="hidden md:flex">
  125. <NavigationMenuList>
  126. <NavigationMenuItem>
  127. <NavigationMenuTrigger className="h-9">
  128. <div className="flex items-center gap-2">
  129. <Image
  130. className="rounded-full"
  131. src={user?.picture || "/default-avatar.png"}
  132. alt="User Avatar"
  133. width={24}
  134. height={24}
  135. />
  136. <span className="text-sm font-medium">{user?.given_name || user?.email}</span>
  137. </div>
  138. </NavigationMenuTrigger>
  139. <NavigationMenuContent>
  140. <ul className="grid gap-3 p-6 w-[300px]">
  141. {userMenuItems.map((item) => (
  142. <li key={item.title}>
  143. <NavigationMenuLink asChild>
  144. <Link
  145. href={item.href}
  146. 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">
  147. <div className="flex items-center gap-2">
  148. <item.icon className="h-4 w-4" />
  149. <div className="text-sm font-medium leading-none">{item.title}</div>
  150. </div>
  151. <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
  152. {item.description}
  153. </p>
  154. </Link>
  155. </NavigationMenuLink>
  156. </li>
  157. ))}
  158. <li>
  159. <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">
  160. <div className="flex items-center gap-2">
  161. <LogOut className="h-4 w-4" />
  162. <div className="text-sm font-medium leading-none">Logout</div>
  163. </div>
  164. <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
  165. Sign out of your account
  166. </p>
  167. </LogoutLink>
  168. </li>
  169. </ul>
  170. </NavigationMenuContent>
  171. </NavigationMenuItem>
  172. </NavigationMenuList>
  173. </NavigationMenu>
  174. </>
  175. ) : (
  176. <div className="hidden md:flex items-center gap-2">
  177. <LoginLink>
  178. <Button variant="ghost" size="sm">
  179. <LogIn className="h-4 w-4 mr-2" />
  180. Login
  181. </Button>
  182. </LoginLink>
  183. <RegisterLink>
  184. <Button size="sm">Sign Up</Button>
  185. </RegisterLink>
  186. </div>
  187. )}
  188. {/* Mobile Menu */}
  189. <Sheet>
  190. <SheetTrigger asChild className="md:hidden">
  191. <Button variant="ghost" size="icon">
  192. <Menu className="h-5 w-5" />
  193. <span className="sr-only">Toggle menu</span>
  194. </Button>
  195. </SheetTrigger>
  196. <SheetContent side="right" className="w-[300px]">
  197. <div className="flex flex-col space-y-4 mt-8">
  198. <Link href="/" className="flex items-center space-x-2">
  199. <Home className="h-4 w-4" />
  200. <span>Home</span>
  201. </Link>
  202. {navigationItems.map((item) => (
  203. <Link
  204. key={item.title}
  205. href={item.href}
  206. className="flex items-center space-x-2">
  207. <item.icon className="h-4 w-4" />
  208. <span>{item.title}</span>
  209. </Link>
  210. ))}
  211. {isAuthenticated ? (
  212. <>
  213. <div className="border-t pt-4">
  214. <div className="flex items-center space-x-2 mb-4">
  215. <Image
  216. className="rounded-full"
  217. src={user?.picture || "/default-avatar.png"}
  218. alt="User Avatar"
  219. width={32}
  220. height={32}
  221. />
  222. <span className="font-medium">{user?.given_name || user?.email}</span>
  223. </div>
  224. {userMenuItems.map((item) => (
  225. <Link
  226. key={item.title}
  227. href={item.href}
  228. className="flex items-center space-x-2 py-2">
  229. <item.icon className="h-4 w-4" />
  230. <span>{item.title}</span>
  231. </Link>
  232. ))}
  233. <LogoutLink className="flex items-center space-x-2 py-2 text-red-600">
  234. <LogOut className="h-4 w-4" />
  235. <span>Logout</span>
  236. </LogoutLink>
  237. </div>
  238. </>
  239. ) : (
  240. <div className="border-t pt-4 space-y-2">
  241. <LoginLink>
  242. <Button variant="ghost" className="w-full justify-start">
  243. <LogIn className="h-4 w-4 mr-2" />
  244. Login
  245. </Button>
  246. </LoginLink>
  247. <RegisterLink>
  248. <Button className="w-full">Sign Up</Button>
  249. </RegisterLink>
  250. </div>
  251. )}
  252. </div>
  253. </SheetContent>
  254. </Sheet>
  255. </div>
  256. </div>
  257. </header>
  258. );
  259. }