import { ActionIcon, Card, Center, Divider, Drawer, type DrawerProps, Group, ScrollArea, Stack, Tabs, Text, TextInput, Tooltip, } from "@mantine/core"; import { IconChartBar, IconClock, IconHistory, IconPin, IconPinFilled, IconSearch, IconTrash, IconTrashX, IconX, } from "@tabler/icons-react"; import { usePubSub } from "create-pubsub/react"; import { useEffect, useState } from "react"; import { useSearchHistory } from "../../../hooks/useSearchHistory"; import type { SearchEntry } from "../../../modules/history"; import { settingsPubSub } from "../../../modules/pubSub"; import { formatRelativeTime } from "../../../modules/stringFormatters"; import SearchStats from "../../Analytics/SearchStats"; interface HistoryDrawerProps extends Omit { onSearchSelect?: (entry: SearchEntry) => void; } export default function HistoryDrawer({ onSearchSelect, ...drawerProps }: HistoryDrawerProps) { const [searchQuery, setSearchQuery] = useState(""); const [activeTab, setActiveTab] = useState("history"); const [pendingDeleteId, setPendingDeleteId] = useState(null); const [settings] = usePubSub(settingsPubSub); const { filteredSearches, groupedSearches, togglePin, deleteEntry, searchHistory, } = useSearchHistory({ limit: 100, enableGrouping: true }); useEffect(() => { searchHistory(searchQuery); }, [searchQuery, searchHistory]); const handleSearchSelect = (search: SearchEntry) => { if (onSearchSelect) { onSearchSelect(search); } }; const handlePin = (searchId: number) => { togglePin(searchId); }; const handleDelete = (searchId: number) => { deleteEntry(searchId); setPendingDeleteId(null); }; const handleDeleteClick = (searchId: number) => { if (pendingDeleteId === searchId) { handleDelete(searchId); } else { setPendingDeleteId(searchId); } }; const cancelDelete = () => { setPendingDeleteId(null); }; const renderSearchItem = (search: SearchEntry, index: number) => ( handleSearchSelect(search)} > {search.query} {formatRelativeTime(search.timestamp)} { e.stopPropagation(); if (search.id) handlePin(search.id); }} aria-label={search.isPinned ? "Unpin search" : "Pin search"} > {search.isPinned ? ( ) : ( )} { e.stopPropagation(); if (search.id) handleDeleteClick(search.id); }} onMouseLeave={() => { if (pendingDeleteId === search.id) { cancelDelete(); } }} aria-label={ pendingDeleteId === search.id ? "Click again to confirm delete" : "Delete search" } style={{ transition: "all 0.2s ease", }} > {pendingDeleteId === search.id ? ( ) : ( )} ); const renderGroupedSearches = () => { const pinnedSearches = filteredSearches.filter((search) => search.isPinned); const unpinnedSearches = filteredSearches.filter( (search) => !search.isPinned, ); const content = []; if (pinnedSearches.length > 0) { content.push( Pinned {pinnedSearches.map(renderSearchItem)} , ); } if ( Object.keys(groupedSearches).length === 0 || unpinnedSearches.length === 0 ) { content.push(...unpinnedSearches.map(renderSearchItem)); } else { const unpinnedGrouped = unpinnedSearches.reduce( (acc, search) => { for (const [period, searches] of Object.entries(groupedSearches)) { if (searches.some((s) => s.id === search.id)) { if (!acc[period]) acc[period] = []; acc[period].push(search); break; } } return acc; }, {} as Record, ); Object.entries(unpinnedGrouped).forEach(([period, searches]) => { if (searches.length > 0) { content.push( {period} {searches.map(renderSearchItem)} , ); } }); } return content; }; if (!settings.enableHistory) { return (
Search history is disabled Enable it in settings to see your recent searches
); } return ( Search History & Analytics } > }> History }> Analytics setSearchQuery(event.currentTarget.value)} leftSection={} rightSection={ searchQuery && ( setSearchQuery("")} > ) } size="sm" style={{ flex: 1 }} /> {filteredSearches.length === 0 ? (
{searchQuery ? "No matching searches found" : "No search history yet"} {!searchQuery && ( Your recent searches will appear here )}
) : ( {renderGroupedSearches()} )}
); }