import { useEffect, useMemo } from 'react' import { Command } from 'cmdk' import { useNavigate } from '@tanstack/react-router' import { useQueryClient } from '@tanstack/react-query' import { Monitor, MapPin, Terminal, FileCode, Download, Bell, BellRing, Users, Building2, Settings, Search, LayoutDashboard, Moon, Sun, PanelLeft, } from 'lucide-react' import { useCommandPalette } from './useCommandPalette' import { useAuth, isSuperAdmin, isTenantAdmin } from '@/lib/auth' import { useUIStore } from '@/lib/store' import type { DeviceListResponse } from '@/lib/api' interface PageCommand { label: string href: string icon: React.FC<{ className?: string }> description?: string visible: boolean } export function CommandPalette() { const { open, setOpen } = useCommandPalette() const navigate = useNavigate() const { user } = useAuth() const { theme, setTheme, toggleSidebar } = useUIStore() const queryClient = useQueryClient() // Global Cmd+K / Ctrl+K listener useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen(!open) } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, [open, setOpen]) const tenantDevicesHref = isSuperAdmin(user) ? '/tenants' : `/tenants/${user?.tenant_id ?? ''}/devices` // Build page commands based on user role const pageCommands: PageCommand[] = useMemo( () => [ { label: 'Dashboard', href: tenantDevicesHref, icon: LayoutDashboard, description: 'Fleet overview', visible: true, }, { label: 'Devices', href: tenantDevicesHref, icon: Monitor, description: 'Device list', visible: true, }, { label: 'Map', href: '/map', icon: MapPin, description: 'Network map', visible: true, }, { label: 'Config Editor', href: '/config-editor', icon: Terminal, description: 'Device configuration', visible: true, }, { label: 'Templates', href: '/templates', icon: FileCode, description: 'Config templates', visible: true, }, { label: 'Firmware', href: '/firmware', icon: Download, description: 'Firmware management', visible: true, }, { label: 'Alerts', href: '/alerts', icon: Bell, description: 'Active alerts', visible: true, }, { label: 'Alert Rules', href: '/alert-rules', icon: BellRing, description: 'Alert rule configuration', visible: true, }, { label: 'Users', href: `/tenants/${user?.tenant_id ?? ''}/users`, icon: Users, description: 'User management', visible: isTenantAdmin(user) && !!user?.tenant_id, }, { label: 'Organizations', href: '/tenants', icon: Building2, description: 'Organization management', visible: isSuperAdmin(user) || isTenantAdmin(user), }, { label: 'Settings', href: '/settings', icon: Settings, description: 'Application settings', visible: true, }, ], [user, tenantDevicesHref], ) // Get cached devices from TanStack Query (try common query key patterns) const devicesCache = useMemo(() => { // Try to find devices data in the query cache const allQueries = queryClient.getQueriesData({ queryKey: ['devices'], }) const devices: Array<{ id: string hostname: string ip_address: string tenant_id: string }> = [] for (const [, data] of allQueries) { if (data && 'items' in data && Array.isArray(data.items)) { for (const device of data.items) { devices.push({ id: device.id, hostname: device.hostname, ip_address: device.ip_address, tenant_id: user?.tenant_id ?? '', }) } } } // Also try fleet summary cache const fleetQueries = queryClient.getQueriesData< Array<{ id: string hostname: string ip_address: string tenant_id: string }> >({ queryKey: ['fleet'] }) for (const [, data] of fleetQueries) { if (Array.isArray(data)) { for (const device of data) { if ( device.id && device.hostname && !devices.some((d) => d.id === device.id) ) { devices.push({ id: device.id, hostname: device.hostname, ip_address: device.ip_address, tenant_id: device.tenant_id ?? user?.tenant_id ?? '', }) } } } } return devices }, [queryClient, user?.tenant_id, open]) const handleSelect = (href: string) => { setOpen(false) void navigate({ to: href }) } const visiblePages = pageCommands.filter((p) => p.visible) const itemClass = 'flex items-center gap-3 px-2 py-2 rounded-lg text-sm text-text-secondary cursor-pointer data-[selected=true]:bg-accent-muted data-[selected=true]:text-accent' const groupHeadingClass = '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:text-text-muted [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider' return (
No results found. {/* Pages group */} {visiblePages.map((page) => { const Icon = page.icon return ( handleSelect(page.href)} className={itemClass} > {page.label} {page.description && ( {page.description} )} ) })} {/* Devices group (from cache) */} {devicesCache.length > 0 && ( {devicesCache.slice(0, 20).map((device) => ( handleSelect( `/tenants/${device.tenant_id}/devices`, ) } className={itemClass} > {device.hostname} {device.ip_address} ))} )} {/* Actions group */} { setTheme(theme === 'dark' ? 'light' : 'dark') setOpen(false) }} className={itemClass} > {theme === 'dark' ? ( ) : ( )} Toggle {theme === 'dark' ? 'Light' : 'Dark'} Mode { toggleSidebar() setOpen(false) }} className={itemClass} > Toggle Sidebar {/* Footer with shortcut hints */}
Navigate with arrow keys Open with Cmd+K
) }