import { useState, useEffect } from "react"; import { useQuery } from "@tanstack/react-query"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Search } from "lucide-react"; import { Link } from "wouter"; import type { MediaOutlet, Article } from "@shared/schema"; interface SearchModalProps { isOpen: boolean; onClose: () => void; } interface SearchResults { outlets: MediaOutlet[]; articles: Article[]; } export default function SearchModal({ isOpen, onClose }: SearchModalProps) { const [searchQuery, setSearchQuery] = useState(""); const [debouncedQuery, setDebouncedQuery] = useState(""); // Debounce search query useEffect(() => { const timer = setTimeout(() => { setDebouncedQuery(searchQuery); }, 300); return () => clearTimeout(timer); }, [searchQuery]); const { data: searchResults, isLoading } = useQuery({ queryKey: ["/api/search", debouncedQuery], queryFn: async () => { if (!debouncedQuery.trim()) { return { outlets: [], articles: [] }; } const res = await fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`); if (!res.ok) { throw new Error(`${res.status}: ${res.statusText}`); } return res.json(); }, enabled: !!debouncedQuery.trim(), }); const totalResults = (searchResults?.outlets.length || 0) + (searchResults?.articles.length || 0); const handleClose = () => { setSearchQuery(""); setDebouncedQuery(""); onClose(); }; const handleLinkClick = () => { handleClose(); }; return ( Search Articles and Media Outlets {/* Search Header */}
setSearchQuery(e.target.value)} className="pl-10 pr-4 border-2 border-blue-200 focus:border-blue-400" data-testid="search-modal-input" autoFocus />
{/* Search Results */}
{!debouncedQuery.trim() ? ( /* Empty State */

Start typing to search articles and outlets

) : isLoading ? ( /* Loading State */
{Array.from({ length: 5 }).map((_, i) => (
))}
) : totalResults === 0 ? ( /* No Results */

No results found for "{debouncedQuery}"

Try a different search term

) : ( /* Search Results */ <>
{totalResults} results found
{/* Outlets Section */} {searchResults && searchResults.outlets.length > 0 && (

Outlets ({searchResults.outlets.length})

{searchResults.outlets.map((outlet) => (
{outlet.imageUrl ? ( {outlet.name} ) : ( {outlet.name.charAt(0)} )}

{outlet.name}

{outlet.category}

{outlet.description || "Media Outlet"}

))}
)} {/* Articles Section */} {searchResults && searchResults.articles.length > 0 && (

Articles ({searchResults.articles.length})

{searchResults.articles.map((article) => (
{article.imageUrl && (
{article.title}
)}

{article.title}

{article.excerpt || ""}

{article.publishedAt && (

{new Date(article.publishedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}

)}
))}
)} )}
); }