"use client"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useAtomValue } from "jotai"; import { Cable, Loader2 } from "lucide-react"; import { useSearchParams } from "next/navigation"; import { type FC, useEffect, useMemo } from "react"; import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { Dialog, DialogContent } from "@/components/ui/dialog"; import { Tabs, TabsContent } from "@/components/ui/tabs"; import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import { useLogsSummary } from "@/hooks/use-logs"; import { connectorsApiService } from "@/lib/apis/connectors-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cn } from "@/lib/utils"; import { ConnectorDialogHeader } from "./connector-popup/components/connector-dialog-header"; import { ConnectorConnectView } from "./connector-popup/connector-configs/views/connector-connect-view"; import { ConnectorEditView } from "./connector-popup/connector-configs/views/connector-edit-view"; import { IndexingConfigurationView } from "./connector-popup/connector-configs/views/indexing-configuration-view"; import { OAUTH_CONNECTORS } from "./connector-popup/constants/connector-constants"; import { useConnectorDialog } from "./connector-popup/hooks/use-connector-dialog"; import { ActiveConnectorsTab } from "./connector-popup/tabs/active-connectors-tab"; import { AllConnectorsTab } from "./connector-popup/tabs/all-connectors-tab"; import { ConnectorAccountsListView } from "./connector-popup/views/connector-accounts-list-view"; import { YouTubeCrawlerView } from "./connector-popup/views/youtube-crawler-view"; export const ConnectorIndicator: FC = () => { const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const searchParams = useSearchParams(); const { data: documentTypeCounts, isLoading: documentTypesLoading } = useAtomValue(documentTypeCountsAtom); // Check if YouTube view is active const isYouTubeView = searchParams.get("view") === "youtube"; // Track active indexing tasks const { summary: logsSummary } = useLogsSummary(searchSpaceId ? Number(searchSpaceId) : 0, 24, { enablePolling: true, refetchInterval: 5000, }); // Use the custom hook for dialog state management const { isOpen, activeTab, connectingId, isScrolled, searchQuery, indexingConfig, indexingConnector, indexingConnectorConfig, editingConnector, connectingConnectorType, isCreatingConnector, startDate, endDate, isStartingIndexing, isSaving, isDisconnecting, periodicEnabled, frequencyMinutes, allConnectors, viewingAccountsType, setSearchQuery, setStartDate, setEndDate, setPeriodicEnabled, setFrequencyMinutes, handleOpenChange, handleTabChange, handleScroll, handleConnectOAuth, handleConnectNonOAuth, handleCreateWebcrawler, handleCreateYouTubeCrawler, handleSubmitConnectForm, handleStartIndexing, handleSkipIndexing, handleStartEdit, handleSaveConnector, handleDisconnectConnector, handleBackFromEdit, handleBackFromConnect, handleBackFromYouTube, handleViewAccountsList, handleBackFromAccountsList, handleQuickIndexConnector, connectorConfig, setConnectorConfig, setIndexingConnectorConfig, setConnectorName, } = useConnectorDialog(); // Fetch connectors using React Query with conditional refetchInterval // This automatically refetches when mutations invalidate the cache (event-driven) // and also polls when dialog is open to catch external changes const { data: connectors = [], isLoading: connectorsLoading, refetch: refreshConnectors, } = useQuery({ queryKey: cacheKeys.connectors.all(searchSpaceId || ""), queryFn: () => connectorsApiService.getConnectors({ queryParams: { search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, }, }), enabled: !!searchSpaceId, staleTime: 5 * 60 * 1000, // 5 minutes (same as connectorsAtom) // Poll when dialog is open to catch external changes refetchInterval: isOpen ? 5000 : false, // 5 seconds when open, no polling when closed }); const queryClient = useQueryClient(); // Also refresh document type counts when dialog is open useEffect(() => { if (!isOpen || !searchSpaceId) return; const POLL_INTERVAL = 5000; // 5 seconds, same as connectors const intervalId = setInterval(() => { // Invalidate document type counts to refresh active document types queryClient.invalidateQueries({ queryKey: cacheKeys.documents.typeCounts(searchSpaceId), }); }, POLL_INTERVAL); // Cleanup interval on unmount or when dialog closes return () => { clearInterval(intervalId); }; }, [isOpen, searchSpaceId, queryClient]); // Get connector IDs that are currently being indexed const indexingConnectorIds = useMemo(() => { if (!logsSummary?.active_tasks) return new Set(); return new Set( logsSummary.active_tasks .filter((task) => task.source?.includes("connector_indexing") && task.connector_id != null) .map((task) => task.connector_id as number) ); }, [logsSummary?.active_tasks]); const isLoading = connectorsLoading || documentTypesLoading; // Get document types that have documents in the search space const activeDocumentTypes = documentTypeCounts ? Object.entries(documentTypeCounts).filter(([, count]) => count > 0) : []; const hasConnectors = connectors.length > 0; const hasSources = hasConnectors || activeDocumentTypes.length > 0; const totalSourceCount = connectors.length + activeDocumentTypes.length; const activeConnectorsCount = connectors.length; // Only actual connectors, not document types // Check which connectors are already connected const connectedTypes = new Set( (allConnectors || []).map((c: SearchSourceConnector) => c.connector_type) ); if (!searchSpaceId) return null; return ( handleOpenChange(true)} > {isLoading ? ( ) : ( <> {activeConnectorsCount > 0 && ( {activeConnectorsCount > 99 ? "99+" : activeConnectorsCount} )} )} {/* YouTube Crawler View - shown when adding YouTube videos */} {isYouTubeView && searchSpaceId ? ( ) : viewingAccountsType ? ( { const oauthConnector = OAUTH_CONNECTORS.find( (c) => c.connectorType === viewingAccountsType.connectorType ); if (oauthConnector) { handleConnectOAuth(oauthConnector); } }} isConnecting={connectingId !== null} /> ) : connectingConnectorType ? ( ) : editingConnector ? ( handleSaveConnector(() => refreshConnectors())} onDisconnect={() => handleDisconnectConnector(() => refreshConnectors())} onBack={handleBackFromEdit} onQuickIndex={ editingConnector.connector_type !== "GOOGLE_DRIVE_CONNECTOR" ? () => handleQuickIndexConnector(editingConnector.id, editingConnector.connector_type) : undefined } onConfigChange={setConnectorConfig} onNameChange={setConnectorName} /> ) : indexingConfig ? ( handleStartIndexing(() => refreshConnectors())} onSkip={handleSkipIndexing} /> ) : ( {/* Header */} {/* Content */}
{/* Bottom fade shadow */}
)}
); };