| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- "use client";
- import Image from "next/image";
- import Link from "next/link";
- import { usePathname } from "next/navigation";
- import { useState } from "react";
- import { useNotifications } from "./NotificationContext";
- import NotificationsPanel from "./NotificationsPanel";
- import ThemeToggle from "./ThemeToggle";
- const nav = [
- { href: "/", label: "Dashboard" },
- { href: "/files", label: "Files" },
- { href: "/duplicates", label: "Duplicates" },
- { href: "/indexing", label: "Indexing" },
- { href: "/tasks", label: "Tasks" },
- { href: "/settings", label: "Settings" },
- ];
- function Header() {
- const [menuOpen, setMenuOpen] = useState(false);
- const [notificationsOpen, setNotificationsOpen] = useState(false);
- const pathname = usePathname();
- const { unreadCount } = useNotifications();
- return (
- <nav className="sticky top-0 z-50 bg-white/80 dark:bg-gray-950/80 backdrop-blur-md border-b border-gray-200/50 dark:border-gray-800/50">
- <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
- <div className="flex h-16 items-center justify-between">
- <div className="flex items-center">
- <div className="flex-shrink-0">
- <Link href="/" className="flex items-center gap-2">
- <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 shadow-sm">
- <svg
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="2"
- className="h-5 w-5 text-white"
- aria-hidden="true"
- >
- <path d="M2.457 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.543-7z" />
- <path d="M12 9a3 3 0 100 6 3 3 0 000-6z" />
- </svg>
- </div>
- <span className="text-lg font-semibold text-gray-900 dark:text-white">
- Watch Turbo
- </span>
- </Link>
- </div>
- <div className="hidden md:block">
- <div className="ml-10 flex items-baseline space-x-1">
- {nav.map((item) => (
- <Link
- key={item.href}
- href={item.href}
- className={`relative rounded-lg px-3 py-2 text-sm font-medium transition-all duration-200 ${
- pathname === item.href
- ? "bg-indigo-50 text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300"
- : "text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-white"
- }`}
- aria-current={pathname === item.href ? "page" : undefined}
- >
- {item.label}
- {pathname === item.href && (
- <div className="absolute inset-x-0 -bottom-px h-px bg-gradient-to-r from-indigo-500 to-purple-500" />
- )}
- </Link>
- ))}
- </div>
- </div>
- </div>
- <div className="hidden md:block">
- <div className="ml-4 flex items-center md:ml-6">
- <ThemeToggle />
- <button
- type="button"
- onClick={() => setNotificationsOpen(true)}
- className="relative rounded-full p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 focus:outline-2 focus:outline-offset-2 focus:outline-indigo-500"
- >
- <span className="absolute -inset-1.5"></span>
- <span className="sr-only">View notifications</span>
- <svg
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="1.5"
- aria-hidden="true"
- className="size-6"
- >
- <path
- d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0"
- strokeLinecap="round"
- strokeLinejoin="round"
- />
- </svg>
- {unreadCount > 0 && (
- <span className="absolute -top-1 -right-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-white transform translate-x-1/2 -translate-y-1/2 bg-red-500 rounded-full min-w-[18px] h-[18px]">
- {unreadCount > 99 ? "99+" : unreadCount}
- </span>
- )}
- </button>
- {/* Profile dropdown placeholder */}
- <div className="relative ml-3">
- <button className="relative flex max-w-xs items-center rounded-full focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500">
- <span className="absolute -inset-1.5"></span>
- <span className="sr-only">Open user menu</span>
- <svg
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="2"
- className="size-8 rounded-full bg-gray-200 dark:bg-gray-700 p-1 text-gray-600 dark:text-gray-300"
- aria-hidden="true"
- >
- <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
- <circle cx="12" cy="7" r="4" />
- </svg>
- </button>
- </div>
- </div>
- </div>
- <div className="-mr-2 flex md:hidden">
- <button
- type="button"
- className="relative inline-flex items-center justify-center rounded-md p-2 text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-200 focus:outline-2 focus:outline-offset-2 focus:outline-indigo-500"
- onClick={() => setMenuOpen((v) => !v)}
- >
- <span className="absolute -inset-0.5"></span>
- <span className="sr-only">Open main menu</span>
- {/* Hamburger icon */}
- <svg
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="1.5"
- aria-hidden="true"
- className={`size-6 ${!menuOpen ? "" : "hidden"}`}
- >
- <path
- d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
- strokeLinecap="round"
- strokeLinejoin="round"
- />
- </svg>
- {/* Close icon */}
- <svg
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="1.5"
- aria-hidden="true"
- className={`size-6 ${menuOpen ? "" : "hidden"}`}
- >
- <path
- d="M6 18 18 6M6 6l12 12"
- strokeLinecap="round"
- strokeLinejoin="round"
- />
- </svg>
- </button>
- </div>
- </div>
- </div>
- {/* Mobile menu */}
- {menuOpen && (
- <div className="md:hidden">
- <div className="space-y-1 px-2 pt-2 pb-3 sm:px-3">
- {nav.map((item) => (
- <Link
- key={item.href}
- href={item.href}
- className={`block rounded-md px-3 py-2 text-base font-medium ${pathname === item.href ? "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white" : "text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white"}`}
- aria-current={pathname === item.href ? "page" : undefined}
- >
- {item.label}
- </Link>
- ))}
- </div>
- <div className="border-t border-gray-200 dark:border-gray-700 pt-4 pb-3">
- <div className="flex items-center px-5">
- <div className="shrink-0">
- <Image
- src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
- alt="User"
- width={40}
- height={40}
- className="size-10 rounded-full outline -outline-offset-1 outline-gray-300 dark:outline-gray-600"
- />
- </div>
- <div className="ml-3">
- <div className="text-base font-medium text-gray-900 dark:text-white">
- Tom Cook
- </div>
- <div className="text-sm font-medium text-gray-500 dark:text-gray-400">
- tom@example.com
- </div>
- </div>
- <button
- type="button"
- onClick={() => setNotificationsOpen(true)}
- className="relative ml-auto shrink-0 rounded-full p-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 focus:outline-2 focus:outline-offset-2 focus:outline-indigo-500"
- >
- <span className="absolute -inset-1.5"></span>
- <span className="sr-only">View notifications</span>
- <svg
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="1.5"
- aria-hidden="true"
- className="size-6"
- >
- <path
- d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0"
- strokeLinecap="round"
- strokeLinejoin="round"
- />
- </svg>
- {unreadCount > 0 && (
- <span className="absolute -top-1 -right-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-white transform translate-x-1/2 -translate-y-1/2 bg-red-500 rounded-full min-w-[18px] h-[18px]">
- {unreadCount > 99 ? "99+" : unreadCount}
- </span>
- )}
- </button>
- </div>
- <div className="mt-3 space-y-1 px-2">
- <Link
- href="#"
- className="block rounded-md px-3 py-2 text-base font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white"
- >
- Your profile
- </Link>
- <Link
- href="#"
- className="block rounded-md px-3 py-2 text-base font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white"
- >
- Settings
- </Link>
- <Link
- href="#"
- className="block rounded-md px-3 py-2 text-base font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white"
- >
- Sign out
- </Link>
- </div>
- </div>
- </div>
- )}
- <div className="flex items-center gap-2 md:hidden px-4 pb-2">
- <ThemeToggle />
- </div>
- <NotificationsPanel
- isOpen={notificationsOpen}
- onClose={() => setNotificationsOpen(false)}
- />
- </nav>
- );
- }
- export default Header;
|