- Add debounced state updates for title and content (500ms delay) - Immediate UI updates with delayed history saving - Prevent one-letter-per-undo issue - Add cleanup for debounce timers on unmount
137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu'
|
|
import { Menu, Search, Archive, StickyNote, Tag, Moon, Sun } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { usePathname } from 'next/navigation'
|
|
import { cn } from '@/lib/utils'
|
|
import { searchNotes } from '@/app/actions/notes'
|
|
import { useRouter } from 'next/navigation'
|
|
|
|
export function Header() {
|
|
const [searchQuery, setSearchQuery] = useState('')
|
|
const [isSearching, setIsSearching] = useState(false)
|
|
const [theme, setTheme] = useState<'light' | 'dark'>('light')
|
|
const pathname = usePathname()
|
|
const router = useRouter()
|
|
|
|
useEffect(() => {
|
|
// Check for saved theme or system preference
|
|
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null
|
|
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
const initialTheme = savedTheme || systemTheme
|
|
|
|
setTheme(initialTheme)
|
|
document.documentElement.classList.toggle('dark', initialTheme === 'dark')
|
|
}, [])
|
|
|
|
const toggleTheme = () => {
|
|
const newTheme = theme === 'light' ? 'dark' : 'light'
|
|
setTheme(newTheme)
|
|
localStorage.setItem('theme', newTheme)
|
|
document.documentElement.classList.toggle('dark', newTheme === 'dark')
|
|
}
|
|
|
|
const handleSearch = async (query: string) => {
|
|
setSearchQuery(query)
|
|
if (query.trim()) {
|
|
setIsSearching(true)
|
|
// Search functionality will be handled by the parent component
|
|
// For now, we'll just update the URL
|
|
router.push(`/?search=${encodeURIComponent(query)}`)
|
|
setIsSearching(false)
|
|
} else {
|
|
router.push('/')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
<div className="container flex h-16 items-center px-4 gap-4">
|
|
{/* Mobile Menu */}
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="sm" className="md:hidden">
|
|
<Menu className="h-5 w-5" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="start" className="w-48">
|
|
<DropdownMenuItem asChild>
|
|
<Link href="/" className="flex items-center">
|
|
<StickyNote className="h-4 w-4 mr-2" />
|
|
Notes
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem asChild>
|
|
<Link href="/archive" className="flex items-center">
|
|
<Archive className="h-4 w-4 mr-2" />
|
|
Archive
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
|
|
{/* Logo */}
|
|
<Link href="/" className="flex items-center gap-2">
|
|
<StickyNote className="h-6 w-6 text-yellow-500" />
|
|
<span className="font-semibold text-xl hidden sm:inline-block">Memento</span>
|
|
</Link>
|
|
|
|
{/* Search Bar */}
|
|
<div className="flex-1 max-w-2xl">
|
|
<div className="relative">
|
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
|
<Input
|
|
placeholder="Search notes..."
|
|
className="pl-10"
|
|
value={searchQuery}
|
|
onChange={(e) => handleSearch(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Theme Toggle */}
|
|
<Button variant="ghost" size="sm" onClick={toggleTheme}>
|
|
{theme === 'light' ? (
|
|
<Moon className="h-5 w-5" />
|
|
) : (
|
|
<Sun className="h-5 w-5" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Desktop Navigation */}
|
|
<nav className="hidden md:flex border-t">
|
|
<Link
|
|
href="/"
|
|
className={cn(
|
|
'flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors hover:bg-accent',
|
|
pathname === '/' && 'bg-accent'
|
|
)}
|
|
>
|
|
<StickyNote className="h-4 w-4" />
|
|
Notes
|
|
</Link>
|
|
<Link
|
|
href="/archive"
|
|
className={cn(
|
|
'flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors hover:bg-accent',
|
|
pathname === '/archive' && 'bg-accent'
|
|
)}
|
|
>
|
|
<Archive className="h-4 w-4" />
|
|
Archive
|
|
</Link>
|
|
</nav>
|
|
</header>
|
|
)
|
|
}
|