diff --git a/services/frontend/src/components/messages/search-overlay.tsx b/services/frontend/src/components/messages/search-overlay.tsx new file mode 100644 index 0000000..154fa18 --- /dev/null +++ b/services/frontend/src/components/messages/search-overlay.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Search, X } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { messagesApi } from "@/lib/api"; +import type { MessageRecord } from "@/lib/types"; + +interface SearchOverlayProps { + open: boolean; + onClose: () => void; + onSelect: (id: string) => void; +} + +export function SearchOverlay({ open, onClose, onSelect }: SearchOverlayProps) { + const [query, setQuery] = useState(""); + const inputRef = useRef(null); + + const { data: results } = useQuery({ + queryKey: ["messages-search", query], + queryFn: async () => { + const res = await messagesApi.search(query, 20); + return res.results; + }, + enabled: query.length >= 2, + }); + + useEffect(() => { + if (open) { + setTimeout(() => inputRef.current?.focus(), 100); + } else { + setQuery(""); + } + }, [open]); + + useEffect(() => { + const handleKey = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === "k") { + e.preventDefault(); + onClose(); // this is called when Cmd+K is pressed globally — toggle + } + if (e.key === "Escape") onClose(); + }; + document.addEventListener("keydown", handleKey); + return () => document.removeEventListener("keydown", handleKey); + }, [onClose]); + + if (!open) return null; + + return ( +
+
+
+ {/* Input */} +
+ + setQuery(e.target.value)} + placeholder="Search messages..." + className="flex-1 bg-transparent text-sm text-text-primary placeholder-text-secondary/40 outline-none" + /> + +
+ + {/* Results */} +
+ {!results || results.length === 0 ? ( +
+ {query.length < 2 ? "Type at least 2 characters" : "No results found"} +
+ ) : ( + results.map((msg) => ( + + )) + )} +
+
+
+ ); +}