"use client" import { useState, useEffect, useRef } from "react" import { useRouter } from "next/navigation" import { Search, LayoutDashboard, Building2, Users, CreditCard, Wrench, FileText, Receipt, BarChart3, Hammer, ClipboardList, Bot, Settings, Zap, Plus, X, ArrowRight, Loader2, } from "lucide-react" import { cn } from "@/lib/utils" const COMMANDS = [ { group: "Navigate", items: [ { label: "Dashboard", href: "/dashboard", icon: LayoutDashboard, keywords: "home overview" }, { label: "Properties", href: "/properties", icon: Building2, keywords: "buildings units" }, { label: "Tenants", href: "/tenants", icon: Users, keywords: "renters residents" }, { label: "Rent Tracker", href: "/rent", icon: CreditCard, keywords: "payments collect" }, { label: "Maintenance", href: "/maintenance", icon: Wrench, keywords: "repair fix" }, { label: "Leases", href: "/leases", icon: FileText, keywords: "contracts agreements" }, { label: "Expenses", href: "/expenses", icon: Receipt, keywords: "costs bills" }, { label: "Reports", href: "/reports", icon: BarChart3, keywords: "analytics profit" }, { label: "Vendors", href: "/vendors", icon: Hammer, keywords: "contractors workers" }, { label: "Inspections", href: "/inspections", icon: ClipboardList, keywords: "checklist condition" }, { label: "AI Assistant", href: "/ai", icon: Bot, keywords: "ask chat" }, { label: "Settings", href: "/settings/profile", icon: Settings, keywords: "profile account" }, ], }, { group: "Quick Actions", items: [ { label: "Add Property", href: "/properties/new", icon: Building2, keywords: "new create" }, { label: "Add Tenant", href: "/tenants/new", icon: Users, keywords: "new create" }, { label: "Record Payment", href: "/rent/new", icon: CreditCard, keywords: "new create" }, { label: "New Maintenance Request",href: "/maintenance/new", icon: Wrench, keywords: "new create" }, { label: "New Lease", href: "/leases/new", icon: FileText, keywords: "new create" }, { label: "Add Expense", href: "/expenses/new", icon: Receipt, keywords: "new create" }, { label: "Generate Rent", href: "/rent/generate", icon: Zap, keywords: "bulk all" }, ], }, ] type CommandItem = (typeof COMMANDS)[0]["items"][0] interface SearchResults { tenants: { id: string; first_name: string; last_name: string; email?: string }[] properties: { id: string; name: string; address_line1?: string; city?: string }[] maintenance: { id: string; title: string; status: string; priority: string }[] } export function CommandPalette() { const [open, setOpen] = useState(false) const [query, setQuery] = useState("") const [cursor, setCursor] = useState(0) const [liveResults, setLiveResults] = useState(null) const [searching, setSearching] = useState(false) const router = useRouter() const inputRef = useRef(null) const listRef = useRef(null) const debounceRef = useRef | null>(null) // Open on Ctrl+K / Cmd+K useEffect(() => { function onKey(e: KeyboardEvent) { if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault() setOpen((v) => !v) } if (e.key === "Escape") setOpen(false) } document.addEventListener("keydown", onKey) return () => document.removeEventListener("keydown", onKey) }, []) useEffect(() => { if (open) { setTimeout(() => inputRef.current?.focus(), 50) setQuery("") setCursor(0) setLiveResults(null) } }, [open]) // Live search debounce useEffect(() => { if (debounceRef.current) clearTimeout(debounceRef.current) if (query.length < 2) { setLiveResults(null); return } setSearching(true) debounceRef.current = setTimeout(async () => { try { const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`) if (res.ok) setLiveResults(await res.json()) } finally { setSearching(false) } }, 300) return () => { if (debounceRef.current) clearTimeout(debounceRef.current) } }, [query]) const filtered: { group: string; items: CommandItem[] }[] = COMMANDS.map((g) => ({ group: g.group, items: g.items.filter((item) => { if (!query) return true const q = query.toLowerCase() return item.label.toLowerCase().includes(q) || item.keywords.includes(q) }), })).filter((g) => g.items.length > 0) const flat: CommandItem[] = filtered.flatMap((g) => g.items) function go(href: string) { router.push(href) setOpen(false) } function onKeyDown(e: React.KeyboardEvent) { if (e.key === "ArrowDown") { e.preventDefault() setCursor((v) => Math.min(v + 1, flat.length - 1)) } else if (e.key === "ArrowUp") { e.preventDefault() setCursor((v) => Math.max(v - 1, 0)) } else if (e.key === "Enter") { if (flat[cursor]) go(flat[cursor].href) } } // Scroll active item into view useEffect(() => { const el = listRef.current?.querySelector(`[data-idx="${cursor}"]`) as HTMLElement el?.scrollIntoView({ block: "nearest" }) }, [cursor]) if (!open) return null let globalIdx = 0 return (
{/* Backdrop */}
setOpen(false)} /> {/* Panel */}
{/* Search input */}
{ setQuery(e.target.value); setCursor(0) }} onKeyDown={onKeyDown} placeholder="Search pages, actions…" className="flex-1 bg-transparent text-sm text-white placeholder-white/30 outline-none" /> {query && ( )} esc
{/* Results */}
{/* Live data results when query length >= 2 */} {query.length >= 2 && ( <> {searching && (
Searching…
)} {liveResults && ( <> {liveResults.tenants.length > 0 && (

Tenants

{liveResults.tenants.map((t) => { const idx = globalIdx++ const active = cursor === idx return ( ) })}
)} {liveResults.properties.length > 0 && (

Properties

{liveResults.properties.map((p) => { const idx = globalIdx++ const active = cursor === idx return ( ) })}
)} {liveResults.maintenance.length > 0 && (

Maintenance

{liveResults.maintenance.map((m) => { const idx = globalIdx++ const active = cursor === idx return ( ) })}
)} {!searching && liveResults.tenants.length === 0 && liveResults.properties.length === 0 && liveResults.maintenance.length === 0 && filtered.length === 0 && (

No results for “{query}”

)} )} {/* Divider before nav items if there are live results */} {liveResults && (liveResults.tenants.length > 0 || liveResults.properties.length > 0 || liveResults.maintenance.length > 0) && filtered.length > 0 && (
)} )} {/* Static command items */} {filtered.length === 0 && query.length < 2 ? (

Type to search…

) : ( filtered.map((group) => (

{group.group}

{group.items.map((item) => { const idx = globalIdx++ const Icon = item.icon const active = cursor === idx return ( ) })}
)) )}
{/* Footer */}
{[["↑↓", "navigate"], ["↵", "open"], ["esc", "close"]].map(([key, label]) => ( {key} {label} ))}
) } // Trigger button for the header export function CommandTrigger() { return ( ) }