diff --git a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx index ac7fb428e..2b57e5e6e 100644 --- a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx @@ -32,7 +32,9 @@ import { Input } from "@/components/ui/input"; import { Spinner } from "@/components/ui/spinner"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import type { InboxItem } from "@/hooks/use-inbox"; +import type { ConnectorIndexingMetadata } from "@/contracts/types/inbox.types"; import { cn } from "@/lib/utils"; /** @@ -84,6 +86,7 @@ export function InboxSidebar({ const [searchQuery, setSearchQuery] = useState(""); const [activeTab, setActiveTab] = useState("mentions"); const [activeFilter, setActiveFilter] = useState("all"); + const [selectedConnector, setSelectedConnector] = useState(null); const [mounted, setMounted] = useState(false); // Dropdown state for filter menu const [openDropdown, setOpenDropdown] = useState<"filter" | null>(null); @@ -114,6 +117,13 @@ export function InboxSidebar({ }; }, [open]); + // Reset connector filter when switching away from status tab + useEffect(() => { + if (activeTab !== "status") { + setSelectedConnector(null); + } + }, [activeTab]); + // Split items by type const mentionItems = useMemo( () => inboxItems.filter((item) => item.type === "new_mention"), @@ -128,18 +138,48 @@ export function InboxSidebar({ [inboxItems] ); + // Get unique connectors from status items for filtering + const uniqueConnectors = useMemo(() => { + const connectorMap = new Map(); + + statusItems + .filter((item) => item.type === "connector_indexing") + .forEach((item) => { + const metadata = item.metadata as ConnectorIndexingMetadata; + if (metadata?.connector_type && !connectorMap.has(metadata.connector_type)) { + connectorMap.set(metadata.connector_type, { + type: metadata.connector_type, + name: metadata.connector_name || metadata.connector_type, + }); + } + }); + + return Array.from(connectorMap.values()); + }, [statusItems]); + // Get items for current tab const currentTabItems = activeTab === "mentions" ? mentionItems : statusItems; - // Filter items based on filter type and search query + // Filter items based on filter type, connector filter, and search query const filteredItems = useMemo(() => { let items = currentTabItems; - // Apply filter + // Apply read/unread filter if (activeFilter === "unread") { items = items.filter((item) => !item.read); } + // Apply connector filter (only for status tab) + if (activeTab === "status" && selectedConnector) { + items = items.filter((item) => { + if (item.type === "connector_indexing") { + const metadata = item.metadata as ConnectorIndexingMetadata; + return metadata?.connector_type === selectedConnector; + } + return false; // Hide document_processing when a specific connector is selected + }); + } + // Apply search query if (searchQuery.trim()) { const query = searchQuery.toLowerCase(); @@ -151,7 +191,7 @@ export function InboxSidebar({ } return items; - }, [currentTabItems, activeFilter, searchQuery]); + }, [currentTabItems, activeFilter, activeTab, selectedConnector, searchQuery]); // Count unread items per tab const unreadMentionsCount = useMemo(() => { @@ -441,6 +481,44 @@ export function InboxSidebar({ + {/* Connector filter chips - only show in status tab when there are connectors */} + {activeTab === "status" && uniqueConnectors.length > 0 && ( +
+ {/* Left shadow indicator */} +
+ {/* Right shadow indicator */} +
+
+ + {uniqueConnectors.map((connector) => ( + + + + + + {connector.name} + + + ))} +
+
+ )} +
{loading ? (