theo-agent-dashboard

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

SearchModal.tsx (2315B)


      1 import { useState, useCallback, useEffect, useRef } from "react";
      2 import { api } from "../../lib/api";
      3 import { SearchBar } from "./SearchBar";
      4 import { SearchResults } from "./SearchResults";
      5 import { useSessionStore } from "../../store/session";
      6 
      7 interface SearchResultMessage {
      8   id: string;
      9   content: string;
     10   role: string;
     11 }
     12 
     13 interface SearchResultGroup {
     14   session_id: string;
     15   session_name: string | null;
     16   messages: SearchResultMessage[];
     17 }
     18 
     19 interface SearchModalProps {
     20   isOpen: boolean;
     21   onClose: () => void;
     22   onSelectSession: (sessionId: string) => void;
     23 }
     24 
     25 export function SearchModal({ isOpen, onClose, onSelectSession }: SearchModalProps) {
     26   const [results, setResults] = useState<SearchResultGroup[]>([]);
     27   const [isSearching, setIsSearching] = useState(false);
     28   const modalRef = useRef<HTMLDivElement>(null);
     29 
     30   const handleSearch = useCallback(async (query: string) => {
     31     if (!query) {
     32       setResults([]);
     33       return;
     34     }
     35     setIsSearching(true);
     36     try {
     37       const data = await api.get<{ results: SearchResultGroup[] }>(
     38         `/search?q=${encodeURIComponent(query)}`,
     39       );
     40       setResults(data.results ?? []);
     41     } catch {
     42       setResults([]);
     43     } finally {
     44       setIsSearching(false);
     45     }
     46   }, []);
     47 
     48   const handleSelectMessage = useCallback(
     49     (sessionId: string, _messageId: string) => {
     50       useSessionStore.getState().setActiveSession(sessionId);
     51       onSelectSession(sessionId);
     52     },
     53     [onSelectSession],
     54   );
     55 
     56   useEffect(() => {
     57     if (!isOpen) return;
     58     const handleKeyDown = (e: KeyboardEvent) => {
     59       if (e.key === "Escape") onClose();
     60     };
     61     document.addEventListener("keydown", handleKeyDown);
     62     return () => document.removeEventListener("keydown", handleKeyDown);
     63   }, [isOpen, onClose]);
     64 
     65   if (!isOpen) return null;
     66 
     67   return (
     68     <div className="fixed inset-0 z-50 flex items-start justify-center bg-black/60 pt-[15vh]">
     69       <div ref={modalRef} className="w-full max-w-lg rounded-xl border border-gray-700 bg-gray-900 shadow-2xl">
     70         <div className="p-3">
     71           <SearchBar onSearch={handleSearch} isSearching={isSearching} />
     72         </div>
     73         <SearchResults
     74           results={results}
     75           onSelectMessage={handleSelectMessage}
     76           onClose={onClose}
     77         />
     78       </div>
     79     </div>
     80   );
     81 }