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}
>
{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
);
}