import { useState, useEffect, useRef } from 'react' import { useNavigate } from 'react-router-dom' import { IconSearch, IconWorld, IconNetwork, IconServer, IconFilter, IconHome, IconDatabase, IconCreditCard, IconDownload, IconKeyboard } from '@tabler/icons-react' /** * Command Palette - глобальный поиск по командам (Ctrl+K) * Простой подход со встроенным backdrop (как в HistoryModal) */ // Создаем контекст для управления открытием/закрытием Command Palette const CommandPaletteContext = { open: null }; function CommandPalette() { const [isOpen, setIsOpen] = useState(false) const [searchTerm, setSearchTerm] = useState('') const [selectedIndex, setSelectedIndex] = useState(0) const navigate = useNavigate() const inputRef = useRef(null) // Сохраняем функцию открытия в контекст useEffect(() => { CommandPaletteContext.open = () => setIsOpen(true); return () => { CommandPaletteContext.open = null; }; }, []) // Список команд const commands = [ { icon: IconHome, label: 'Главная', description: 'Панель управления', action: () => navigate('/dashboard'), keywords: ['главная', 'панель', 'dashboard'] }, { icon: IconWorld, label: 'Домены', description: 'Управление доменами', action: () => navigate('/domains'), keywords: ['домены', 'domains'] }, { icon: IconNetwork, label: 'IP-диапазоны', description: 'Управление IP диапазонами', action: () => navigate('/ip-ranges'), keywords: ['ip', 'диапазоны', 'ranges'] }, { icon: IconNetwork, label: 'ASN', description: 'Управление Autonomous Systems', action: () => navigate('/asns'), keywords: ['asn', 'as', 'autonomous'] }, { icon: IconFilter, label: 'Community', description: 'Справочник BGP Community', action: () => navigate('/communities'), keywords: ['community', 'справочник'] }, { icon: IconServer, label: 'Серверы', description: 'Управление серверами', action: () => navigate('/servers'), keywords: ['серверы', 'servers'] }, { icon: IconFilter, label: 'Фильтры', description: 'Filter Manager', action: () => navigate('/filters'), keywords: ['фильтры', 'filters', 'mikrotik'] }, { icon: IconCreditCard, label: 'Биллинг', description: 'Управление биллингом', action: () => navigate('/billing'), keywords: ['биллинг', 'billing', 'оплата'] }, { icon: IconDownload, label: 'Авто-URL', description: 'Генератор ссылок', action: () => navigate('/auto-urls'), keywords: ['url', 'ссылки', 'генератор'] }, ] // Фильтрация команд по поисковому запросу const filteredCommands = searchTerm.trim() === '' ? commands : commands.filter(cmd => cmd.label.toLowerCase().includes(searchTerm.toLowerCase()) || cmd.description.toLowerCase().includes(searchTerm.toLowerCase()) || cmd.keywords.some(kw => kw.includes(searchTerm.toLowerCase())) ) // Открытие/закрытие по Ctrl+K или Cmd+K useEffect(() => { const handleKeyDown = (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault() setIsOpen(prev => !prev) } if (e.key === 'Escape') { setIsOpen(false) } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, []) // Навигация по списку стрелками useEffect(() => { if (!isOpen) return const handleKeyDown = (e) => { if (e.key === 'ArrowDown') { e.preventDefault() setSelectedIndex(prev => Math.min(prev + 1, filteredCommands.length - 1)) } else if (e.key === 'ArrowUp') { e.preventDefault() setSelectedIndex(prev => Math.max(prev - 1, 0)) } else if (e.key === 'Enter') { e.preventDefault() if (filteredCommands[selectedIndex]) { executeCommand(filteredCommands[selectedIndex]) } } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, [isOpen, selectedIndex, filteredCommands]) // Фокус на input при открытии useEffect(() => { if (isOpen) { setSearchTerm('') setSelectedIndex(0) setTimeout(() => inputRef.current?.focus(), 50) } }, [isOpen]) const executeCommand = (command) => { command.action() setIsOpen(false) } if (!isOpen) return null return (