/* CommandPalette.jsx — ⌘K, live API замість MOCK_*. */ const { useState: useStateCP, useEffect: useEffectCP, useRef: useRefCP } = React; const NAV_PALETTE = [ { kind: "nav", label: "Огляд", icon: "◉", screen: "overview" }, { kind: "nav", label: "Зупинки", icon: "📍", screen: "stops" }, { kind: "nav", label: "Маршрути", icon: "🛤", screen: "routes" }, { kind: "nav", label: "Моніторинг", icon: "📡", screen: "monitoring" }, { kind: "nav", label: "Аналітика", icon: "📊", screen: "analytics" }, { kind: "nav", label: "Історія змін", icon: "🕓", screen: "history" }, { kind: "nav", label: "Налаштування", icon: "⚙", screen: "settings" }, ]; const ACTIONS_PALETTE = [ { kind: "action", label: "Нова зупинка", icon: "➕", fn: (nav) => { nav("stops"); window.showToast && window.showToast("Натисніть «+ Нова зупинка»","info"); } }, { kind: "action", label: "Новий маршрут", icon: "➕", fn: (nav) => { nav("routes"); window.showToast && window.showToast("Натисніть «+ Новий маршрут»","info"); } }, { kind: "action", label: "Логаут", icon: "⏻", fn: async () => { try { await window.passengerApi.auth.logout(); window.location.reload(); } catch {} } }, ]; const KIND_LABELS = { nav: "Перейти", action: "Дія", stop: "Зупинка", route: "Маршрут" }; function CommandPalette({ open, onClose, onNav }) { const [q, setQ] = useStateCP(""); const [sel, setSel] = useStateCP(0); const [stopsItems, setStopsItems] = useStateCP([]); const [routesItems, setRoutesItems] = useStateCP([]); const inputRef = useRefCP(); // При відкритті — підтягуємо stops + routes (limit 50 кожен). useEffectCP(() => { if (!open) return; setQ(""); setSel(0); setTimeout(() => inputRef.current?.focus(), 30); let cancelled = false; (async () => { try { const [s, r] = await Promise.allSettled([ window.passengerApi.stops.list({ limit: 50 }), window.passengerApi.routes.list({ limit: 50 }), ]); if (cancelled) return; if (s.status === "fulfilled") { setStopsItems((s.value.items || []).map(it => ({ kind: "stop", icon: "📍", label: it.name, hint: `${it.code} · ${it.radius}м · ${it.status}`, screen: "stops", }))); } if (r.status === "fulfilled") { setRoutesItems((r.value.items || []).map(it => ({ kind: "route", icon: "🛤", label: `${it.code} ${it.name}`, hint: it.paused ? "⏸ призупинено" : `v${it.version} · ${it.status}`, screen: "routes", }))); } } catch (e) { /* swallow */ } })(); return () => { cancelled = true; }; }, [open]); useEffectCP(() => { const handler = (e) => { if (e.key === "Escape" && open) { e.preventDefault(); onClose(); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [open, onClose]); const all = [...NAV_PALETTE, ...ACTIONS_PALETTE, ...stopsItems, ...routesItems]; const filtered = q ? all.filter(i => i.label.toLowerCase().includes(q.toLowerCase()) || (i.hint || "").toLowerCase().includes(q.toLowerCase())) : all; useEffectCP(() => setSel(0), [q]); const activate = (item) => { if (item.fn) item.fn(onNav); else if (item.screen) onNav(item.screen); onClose(); }; const handleKey = (e) => { if (e.key === "ArrowDown") { e.preventDefault(); setSel(s => Math.min(s + 1, filtered.length - 1)); } if (e.key === "ArrowUp") { e.preventDefault(); setSel(s => Math.max(s - 1, 0)); } if (e.key === "Enter" && filtered[sel]) activate(filtered[sel]); }; const groups = filtered.reduce((acc, item) => { (acc[item.kind] = acc[item.kind] || []).push(item); return acc; }, {}); let globalIdx = 0; if (!open) return null; return (