"use client"; import { useState, useRef, useEffect, useMemo } from "react"; import { trpc } from "~/lib/trpc/client.js"; import { useDebounce } from "~/hooks/useDebounce.js"; interface ResourceComboboxProps { value: string | null; onChange: (id: string | null) => void; placeholder?: string; disabled?: boolean; isActive?: boolean; className?: string; } export function ResourceCombobox({ value, onChange, placeholder = "Search resource…", disabled = false, isActive = true, className = "", }: ResourceComboboxProps) { const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); const debouncedSearch = useDebounce(search, 300); const containerRef = useRef(null); const inputRef = useRef(null); const { data } = trpc.resource.list.useQuery( { search: debouncedSearch || undefined, limit: 15, isActive }, { enabled: open, staleTime: 30_000 }, ); const resources = data?.resources ?? []; // Resolve display name for currently selected value const { data: selectedData } = trpc.resource.list.useQuery( { search: undefined, limit: 500, isActive: undefined as unknown as boolean }, { enabled: !!value && !open, staleTime: 60_000 }, ); const selectedLabel = useMemo(() => { if (!value) return ""; const fromOpen = resources.find((r) => r.id === value); if (fromOpen) return `${fromOpen.displayName} (${fromOpen.eid})`; const fromSelected = selectedData?.resources.find((r) => r.id === value); if (fromSelected) return `${fromSelected.displayName} (${fromSelected.eid})`; return value; }, [value, resources, selectedData]); useEffect(() => { if (!open) return; function handleClick(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setOpen(false); setSearch(""); } } document.addEventListener("mousedown", handleClick); return () => document.removeEventListener("mousedown", handleClick); }, [open]); function handleOpen() { if (disabled) return; setOpen(true); setSearch(""); setTimeout(() => inputRef.current?.focus(), 0); } function select(id: string | null) { onChange(id); setOpen(false); setSearch(""); } return (
{open && (
setSearch(e.target.value)} placeholder="Type to search…" className="w-full px-2 py-1 text-sm border-0 outline-none" />
    {resources.length === 0 ? (
  • No results
  • ) : ( resources.map((r) => (
  • )) )}
)}
); }