passmaster/src/components/layout/Header.tsx

149 lines
5.5 KiB
TypeScript

"use client"
import { useState, useEffect } from 'react'
import { Shield, Sun, Moon, Download, Menu, X } from 'lucide-react'
import { useTheme } from 'next-themes'
import { motion } from 'framer-motion'
import Link from 'next/link'
export function Header() {
const { theme, setTheme } = useTheme()
const [mounted, setMounted] = useState(false)
const [showInstallPrompt, setShowInstallPrompt] = useState(false)
const [deferredPrompt, setDeferredPrompt] = useState<any>(null)
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
useEffect(() => {
setMounted(true)
// Listen for PWA install prompt
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()
setDeferredPrompt(e)
setShowInstallPrompt(true)
})
}, [])
const handleInstallClick = async () => {
if (deferredPrompt) {
deferredPrompt.prompt()
const { outcome } = await deferredPrompt.userChoice
if (outcome === 'accepted') {
setShowInstallPrompt(false)
setDeferredPrompt(null)
}
}
}
const toggleTheme = () => {
setTheme(theme === 'dark' ? 'light' : 'dark')
}
if (!mounted) {
return null
}
return (
<header className="sticky top-0 z-50 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-700">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<motion.div
className="flex items-center space-x-2"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Link href="/" className="flex items-center space-x-2 hover:opacity-80 transition-opacity">
<Shield className="h-8 w-8 text-primary-600" />
<span className="text-xl font-bold text-gray-900 dark:text-white">
PassMaster
</span>
</Link>
</motion.div>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center space-x-4">
<Link
href="/privacy"
className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200 px-3 py-2 rounded-md text-sm font-medium"
>
Privacy
</Link>
{showInstallPrompt && (
<motion.button
onClick={handleInstallClick}
className="btn-secondary flex items-center space-x-2"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3 }}
>
<Download className="h-4 w-4" />
<span>Install App</span>
</motion.button>
)}
<button
onClick={toggleTheme}
className="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200"
aria-label="Toggle theme"
>
{theme === 'dark' ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
</button>
</nav>
{/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200"
aria-label="Toggle mobile menu"
>
{mobileMenuOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</button>
</div>
</div>
{/* Mobile Navigation */}
{mobileMenuOpen && (
<motion.div
className="md:hidden py-4 border-t border-gray-200 dark:border-gray-700"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3 }}
>
<div className="flex flex-col space-y-3">
<Link
href="/privacy"
className="flex items-center justify-center px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200"
onClick={() => setMobileMenuOpen(false)}
>
Privacy Policy
</Link>
{showInstallPrompt && (
<button
onClick={handleInstallClick}
className="btn-secondary flex items-center justify-center space-x-2"
>
<Download className="h-4 w-4" />
<span>Install App</span>
</button>
)}
<button
onClick={toggleTheme}
className="flex items-center justify-center space-x-2 p-3 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200"
>
{theme === 'dark' ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
<span>Toggle Theme</span>
</button>
</div>
</motion.div>
)}
</div>
</header>
)
}