import { Input } from "./ui/input"; import { useStore } from "@/store"; import { Grid, List, SearchIcon, XIcon } from "lucide-react"; import { Button } from "./ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "./ui/command"; import { cn } from "@/lib/utils"; import { Check, ChevronsUpDown } from "lucide-react"; import React from "react"; import { Tabs, TabsList, TabsTrigger } from "./ui/tabs"; import SelectedTags from "./SelectedTags"; import { useSearchParams } from "react-router-dom"; const Search = () => { const { templates, searchQuery, setSearchQuery, setView, templatesCount, setFilteredTemplates, setTemplatesCount } = useStore(); const selectedTags = useStore((state) => state.selectedTags); const addSelectedTag = useStore((state) => state.addSelectedTag); const removeSelectedTag = useStore((state) => state.removeSelectedTag); const [open, setOpen] = React.useState(false); const [tagSearch, setTagSearch] = React.useState(""); const view = useStore((state) => state.view); const [searchParams, setSearchParams] = useSearchParams(); // Get all unique tags, safely handle empty templates const uniqueTags = React.useMemo(() => { if (!templates || templates.length === 0) return []; return Array.from( new Set(templates.flatMap((template) => template.tags || [])) ).sort(); }, [templates]); // Filter tags based on search const filteredTags = React.useMemo(() => { if (!tagSearch) return uniqueTags; return uniqueTags.filter((tag) => tag.toLowerCase().includes(tagSearch.toLowerCase()) ); }, [uniqueTags, tagSearch]); // Initialize search query from URL params and apply filters React.useEffect(() => { const queryFromUrl = searchParams.get("q") || ""; if (queryFromUrl !== searchQuery) { setSearchQuery(queryFromUrl); } // Apply filters whenever templates, search query or selected tags change if (templates) { const filtered = templates.filter((template) => { // Filter by search query - search across name and description const searchTerm = queryFromUrl.toLowerCase(); const matchesSearch = template.name.toLowerCase().includes(searchTerm) || template.description.toLowerCase().includes(searchTerm); // Filter by selected tags const matchesTags = selectedTags.length === 0 || selectedTags.every((tag) => template.tags.includes(tag)); return matchesSearch && matchesTags; }); setFilteredTemplates(filtered); setTemplatesCount(filtered.length); } }, [searchParams, templates, selectedTags, setSearchQuery, setFilteredTemplates, setTemplatesCount]); // Update URL params when search query changes const handleSearchChange = (e: React.ChangeEvent) => { const newQuery = e.target.value; setSearchQuery(newQuery); if (newQuery) { setSearchParams({ q: newQuery }); } else { searchParams.delete("q"); setSearchParams(searchParams); } }; // Clear search and URL params const handleClearSearch = () => { setSearchQuery(""); searchParams.delete("q"); setSearchParams(searchParams); }; return (
{/*

Available Templates ({templates?.length || 0})

*/}
Available Templates
{(templatesCount && templatesCount) || 0}
{searchQuery.length > 0 ? (
) : ( )}
{ setView(value as "grid" | "rows"); }} > Grid List No tags found. {filteredTags.map((tag) => ( { if (selectedTags.includes(value)) { removeSelectedTag(value); } else { addSelectedTag(value); } }} > {tag} ))}
{selectedTags.length > 0 && }
); }; export default Search;