import React, { useState, useEffect, useMemo } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { Search, Command, X, Plus, Moon, Sun, LogOut, Palette } from 'lucide-react'; interface CommandItem { id: string; label: string; path?: string; action?: () => void; icon: React.ReactNode; category: string; } export default function CommandPalette({ onQuickAdd, toggleTheme, onLogout }: { onQuickAdd?: () => void; toggleTheme?: () => void; onLogout?: () => void; }) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(''); const [selectedIndex, setSelectedIndex] = useState(0); const navigate = useNavigate(); const COMMAND_ITEMS: CommandItem[] = useMemo(() => [ { id: 'dash', label: 'Dashboard', path: '/', icon: 📊, category: 'General' }, { id: 'time', label: 'Time Entries', path: '/time-entries', icon: ⏱️, category: 'Tracking' }, { id: 'cust', label: 'Customers', path: '/customers', icon: 👥, category: 'CRM' }, { id: 'proj', label: 'Projects', path: '/projects', icon: 📁, category: 'CRM' }, { id: 'cal', label: 'Calendar', path: '/calendar', icon: 📅, category: 'General' }, { id: 'sett', label: 'Settings', path: '/settings', icon: ⚙️, category: 'System' }, { id: 'prof', label: 'Profile', path: '/profile', icon: 👤, category: 'System' }, { id: 'add-time', label: 'Neuer Time-Entry', action: onQuickAdd, icon: , category: 'Actions' }, { id: 'toggle-theme', label: 'Dark/Light umschalten', action: toggleTheme, icon: , category: 'Actions' }, { id: 'change-theme', label: 'Theme wechseln', action: () => {}, icon: , category: 'Actions' }, { id: 'logout', label: 'Logout', action: onLogout, icon: , category: 'Actions' }, ], [onQuickAdd, toggleTheme, onLogout]); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); setIsOpen((prev) => !prev); } if (e.key === 'Escape' && isOpen) { setIsOpen(false); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [isOpen]); const filteredItems = useMemo(() => { return COMMAND_ITEMS.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()) ); }, [query, COMMAND_ITEMS]); useEffect(() => { setSelectedIndex(0); }, [query]); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedIndex((prev) => (prev + 1) % filteredItems.length); } else if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedIndex((prev) => (prev - 1 + filteredItems.length) % filteredItems.length); } else if (e.key === 'Enter' && filteredItems[selectedIndex]) { const item = filteredItems[selectedIndex]; if (item.path) { navigate({ to: item.path }); } else if (item.action) { item.action(); } setIsOpen(false); setQuery(''); } }; if (!isOpen) return null; return (
setIsOpen(false)} >
e.stopPropagation()} onKeyDown={handleKeyDown} tabIndex={-1} >
setQuery(e.target.value)} />
K
{filteredItems.length > 0 ? (
{['Actions', 'General', 'Tracking', 'CRM', 'System'].map(cat => { const catItems = filteredItems.filter(i => i.category === cat); if (catItems.length === 0) return null; return (
{cat}
{catItems.map((item) => { const isSelected = filteredItems[selectedIndex]?.id === item.id; return (
{ if (item.path) navigate({ to: item.path }); if (item.action) item.action(); setIsOpen(false); setQuery(''); }} >
{item.icon} {item.label}
{item.path && {item.path}}
); })}
); })}
) : (
No results found for "{query}"
)}
Use ↑↓ to navigate and Enter to select
); }