Spaces:
Sleeping
Sleeping
| 'use client'; | |
| import { useState, useEffect } from 'react'; | |
| import { useRouter, usePathname } from 'next/navigation'; | |
| import { motion, AnimatePresence } from 'framer-motion'; | |
| import { logout } from '@/lib/api'; | |
| import Link from 'next/link'; | |
| import { | |
| LogOut, | |
| User, | |
| ListTodo, | |
| BarChart3, | |
| FolderOpen, | |
| Calendar, | |
| PieChart, | |
| MessageSquare, | |
| Zap, | |
| Shield, | |
| Bell, | |
| AlertCircle, | |
| Clock | |
| } from 'lucide-react'; | |
| import { getTasks } from '@/lib/api'; | |
| import { showToast } from '@/lib/toast'; | |
| interface DesignerHeaderProps { | |
| children?: React.ReactNode; | |
| } | |
| export default function DesignerHeader({ children }: DesignerHeaderProps) { | |
| const router = useRouter(); | |
| const pathname = usePathname(); | |
| const [showNotifications, setShowNotifications] = useState(false); | |
| const [notifications, setNotifications] = useState([ | |
| { id: 1, title: 'Welcome to TaskFlow', desc: 'System initialized and ready.', time: 'Now', icon: Bell, color: 'text-indigo-400' }, | |
| { id: 2, title: 'Security Protocol', desc: 'Secure connection established.', time: '1h ago', icon: Shield, color: 'text-emerald-400' }, | |
| { id: 3, title: 'Achievement Unlocked', desc: 'Productivity streak started!', time: '2h ago', icon: Zap, color: 'text-yellow-400' }, | |
| ]); | |
| useEffect(() => { | |
| const checkTaskAlerts = async () => { | |
| const userStr = localStorage.getItem('user'); | |
| if (!userStr) return; | |
| try { | |
| const user = JSON.parse(userStr); | |
| const tasks = await getTasks(user.id); | |
| const today = new Date().toISOString().split('T')[0]; | |
| const dueTodayTasks = tasks.filter(t => !t.completed && t.due_date && t.due_date.startsWith(today)); | |
| const overdueTasks = tasks.filter(t => !t.completed && t.due_date && new Date(t.due_date) < new Date(today)); | |
| if (dueTodayTasks.length > 0 || overdueTasks.length > 0) { | |
| const newAlerts = [ | |
| ...dueTodayTasks.map(t => ({ | |
| id: `due-${t.id}`, | |
| title: 'Task Due Today', | |
| desc: t.title, | |
| time: 'Now', | |
| icon: Clock, | |
| color: 'text-yellow-400' | |
| })), | |
| ...overdueTasks.map(t => ({ | |
| id: `overdue-${t.id}`, | |
| title: 'Task Overdue', | |
| desc: t.title, | |
| time: 'Past Due', | |
| icon: AlertCircle, | |
| color: 'text-red-400' | |
| })) | |
| ]; | |
| setNotifications(prev => { | |
| // Avoid duplicates | |
| const existingIds = new Set(prev.map(n => n.id)); | |
| const filteredNew = newAlerts.filter(n => !existingIds.has(n.id)); | |
| return [...filteredNew, ...prev]; | |
| }); | |
| if (dueTodayTasks.length > 0) { | |
| showToast.warning(`You have ${dueTodayTasks.length} task(s) due today!`); | |
| } | |
| if (overdueTasks.length > 0) { | |
| showToast.error(`You have ${overdueTasks.length} overdue task(s)!`); | |
| } | |
| } | |
| } catch (err) { | |
| console.error('Failed to check task alerts:', err); | |
| } | |
| }; | |
| checkTaskAlerts(); | |
| // Check every 5 minutes | |
| const interval = setInterval(checkTaskAlerts, 5 * 60 * 1000); | |
| return () => clearInterval(interval); | |
| }, []); | |
| const handleLogout = async () => { | |
| try { | |
| await logout(); | |
| router.push('/login'); | |
| router.refresh(); | |
| } catch (error) { | |
| console.error('Logout error:', error); | |
| router.push('/login'); | |
| } | |
| }; | |
| const navItems = [ | |
| { name: 'Dashboard', href: '/tasks', icon: BarChart3 }, | |
| { name: 'Projects', href: '/projects', icon: FolderOpen }, | |
| { name: 'Calendar', href: '/calendar', icon: Calendar }, | |
| { name: 'Analytics', href: '/analytics', icon: PieChart }, | |
| { name: 'AI Chat', href: '/chat', icon: MessageSquare }, | |
| ]; | |
| const isActive = (path: string) => pathname === path; | |
| return ( | |
| <header className="sticky top-0 z-50 w-full bg-slate-950/80 backdrop-blur-xl border-b border-white/5"> | |
| <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div className="flex justify-between h-20 items-center"> | |
| <div className="flex items-center space-x-8"> | |
| <Link href="/" className="flex items-center group"> | |
| <div className="w-10 h-10 rounded-xl bg-gradient-to-tr from-indigo-600 to-purple-600 flex items-center justify-center shadow-lg shadow-indigo-500/20 group-hover:scale-105 transition-transform"> | |
| <ListTodo className="h-6 w-6 text-white" /> | |
| </div> | |
| <span className="ml-3 text-2xl font-black bg-clip-text text-transparent bg-gradient-to-r from-white to-white/60 tracking-tight">TaskFlow</span> | |
| </Link> | |
| <nav className="hidden lg:flex items-center space-x-1"> | |
| {navItems.map((item) => ( | |
| <Link | |
| key={item.href} | |
| href={item.href} | |
| className={`px-4 py-2 rounded-xl text-sm font-semibold transition-all flex items-center gap-2 ${isActive(item.href) | |
| ? 'bg-white/10 text-white border border-white/10' | |
| : 'text-slate-400 hover:text-white hover:bg-white/5' | |
| }`} | |
| > | |
| <item.icon className={`h-4 w-4 ${isActive(item.href) ? 'text-indigo-400' : ''}`} /> | |
| {item.name} | |
| </Link> | |
| ))} | |
| </nav> | |
| </div> | |
| <div className="flex items-center space-x-4"> | |
| {children} | |
| <div className="relative"> | |
| <button | |
| onClick={() => setShowNotifications(!showNotifications)} | |
| className={`p-2.5 rounded-xl transition-all relative border ${showNotifications ? 'bg-indigo-500/10 border-indigo-500/50 text-white' : 'text-slate-400 hover:text-white bg-white/5 border-white/10'}`} | |
| > | |
| <Bell className="h-5 w-5" /> | |
| {notifications.length > 0 && ( | |
| <span className="absolute top-2 right-2 w-2 h-2 bg-indigo-500 rounded-full border-2 border-slate-950" /> | |
| )} | |
| </button> | |
| <AnimatePresence> | |
| {showNotifications && ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 10, scale: 0.95 }} | |
| animate={{ opacity: 1, y: 0, scale: 1 }} | |
| exit={{ opacity: 0, y: 10, scale: 0.95 }} | |
| className="absolute right-0 mt-4 w-80 bg-[#0a0a0a]/90 backdrop-blur-2xl border border-white/10 rounded-3xl shadow-[0_20px_50px_rgba(0,0,0,0.5)] overflow-hidden z-50 text-left" | |
| > | |
| <div className="p-6 border-b border-white/5 flex items-center justify-between"> | |
| <h3 className="text-xs font-black uppercase tracking-[0.2em] text-white/60">Notifications</h3> | |
| <button onClick={() => setNotifications([])} className="text-[10px] font-black text-indigo-400 uppercase hover:text-indigo-300 transition-colors">Clear All</button> | |
| </div> | |
| <div className="max-h-96 overflow-y-auto"> | |
| {notifications.length > 0 ? notifications.map((n) => ( | |
| <div key={n.id} className="p-5 border-b border-white/5 hover:bg-white/[0.03] transition-all group cursor-pointer"> | |
| <div className="flex gap-4"> | |
| <div className={`w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center flex-shrink-0 border border-white/5 group-hover:border-white/10 transition-all ${n.color}`}> | |
| <n.icon className="w-5 h-5" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="flex justify-between items-start mb-1"> | |
| <h4 className="text-xs font-black text-white group-hover:text-indigo-400 transition-colors uppercase tracking-tight">{n.title}</h4> | |
| <span className="text-[9px] font-bold text-white/20 uppercase whitespace-nowrap">{n.time}</span> | |
| </div> | |
| <p className="text-[10px] text-slate-400 leading-relaxed font-medium">{n.desc}</p> | |
| </div> | |
| </div> | |
| </div> | |
| )) : ( | |
| <div className="p-12 text-center"> | |
| <Bell className="w-6 h-6 text-white/10 mx-auto mb-4" /> | |
| <p className="text-[10px] font-black text-white/20 uppercase tracking-widest">No active signals</p> | |
| </div> | |
| )} | |
| </div> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| <button | |
| onClick={() => router.push('/profile')} | |
| className={`w-10 h-10 rounded-full border flex items-center justify-center transition-all ${isActive('/profile') ? 'border-indigo-500 bg-indigo-500/10 text-white shadow-[0_0_15px_rgba(99,102,241,0.3)]' : 'border-white/10 bg-white/5 text-slate-300 hover:bg-white/10 hover:text-white'}`} | |
| > | |
| <User className="h-5 w-5" /> | |
| </button> | |
| <button | |
| onClick={handleLogout} | |
| className="hidden sm:block px-5 py-2.5 text-sm font-bold text-white bg-white/5 hover:bg-red-500/20 border border-white/10 hover:border-red-500/30 rounded-xl transition-all" | |
| > | |
| Logout | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| ); | |
| } | |