2025-12-30 15:51:10 +05:30
"use client" ;
2026-03-16 21:10:46 +05:30
import { useAtomValue , useSetAtom } from "jotai" ;
2026-02-01 19:56:03 -08:00
import { AlertTriangle , Cable , Settings } from "lucide-react" ;
2026-03-18 16:08:30 +05:30
import { forwardRef , useEffect , useImperativeHandle , useMemo , useState } from "react" ;
2026-03-19 17:51:59 +05:30
import { createPortal } from "react-dom" ;
2026-02-28 01:54:54 -08:00
import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms" ;
2026-03-10 23:06:33 +02:00
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom" ;
2026-02-01 19:56:03 -08:00
import {
globalNewLLMConfigsAtom ,
llmPreferencesAtom ,
} from "@/atoms/new-llm-config/new-llm-config-query.atoms" ;
2025-12-30 04:03:34 +05:30
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms" ;
2026-03-16 21:10:46 +05:30
import { searchSpaceSettingsDialogAtom } from "@/atoms/settings/settings-dialog.atoms" ;
2026-01-23 18:57:10 +05:30
import { currentUserAtom } from "@/atoms/user/user-query.atoms" ;
2026-01-01 22:56:37 -08:00
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button" ;
2026-02-01 19:56:03 -08:00
import { Alert , AlertDescription , AlertTitle } from "@/components/ui/alert" ;
import { Button } from "@/components/ui/button" ;
2026-01-23 20:19:04 +05:30
import { Dialog , DialogContent , DialogTitle } from "@/components/ui/dialog" ;
2026-01-25 15:23:45 +05:30
import { Spinner } from "@/components/ui/spinner" ;
2026-01-01 22:24:42 +05:30
import { Tabs , TabsContent } from "@/components/ui/tabs" ;
2026-01-01 22:56:37 -08:00
import type { SearchSourceConnector } from "@/contracts/types/connector.types" ;
2026-01-16 11:32:06 -08:00
import { useConnectorsElectric } from "@/hooks/use-connectors-electric" ;
2026-03-10 23:06:33 +02:00
import { PICKER_CLOSE_EVENT , PICKER_OPEN_EVENT } from "@/hooks/use-google-picker" ;
2025-12-30 20:07:15 +05:30
import { cn } from "@/lib/utils" ;
2025-12-31 02:00:11 +05:30
import { ConnectorDialogHeader } from "./connector-popup/components/connector-dialog-header" ;
2025-12-31 12:35:43 +05:30
import { ConnectorConnectView } from "./connector-popup/connector-configs/views/connector-connect-view" ;
2026-01-01 22:56:37 -08:00
import { ConnectorEditView } from "./connector-popup/connector-configs/views/connector-edit-view" ;
2025-12-31 02:00:11 +05:30
import { IndexingConfigurationView } from "./connector-popup/connector-configs/views/indexing-configuration-view" ;
2026-01-24 04:36:34 +05:30
import {
COMPOSIO_CONNECTORS ,
OAUTH_CONNECTORS ,
} from "./connector-popup/constants/connector-constants" ;
2025-12-31 02:00:11 +05:30
import { useConnectorDialog } from "./connector-popup/hooks/use-connector-dialog" ;
2026-01-16 11:32:06 -08:00
import { useIndexingConnectors } from "./connector-popup/hooks/use-indexing-connectors" ;
2026-01-01 22:56:37 -08:00
import { ActiveConnectorsTab } from "./connector-popup/tabs/active-connectors-tab" ;
import { AllConnectorsTab } from "./connector-popup/tabs/all-connectors-tab" ;
2026-01-07 09:28:07 +02:00
import { ConnectorAccountsListView } from "./connector-popup/views/connector-accounts-list-view" ;
2026-01-01 22:56:37 -08:00
import { YouTubeCrawlerView } from "./connector-popup/views/youtube-crawler-view" ;
2025-12-30 15:51:10 +05:30
2026-03-09 23:18:23 -07:00
export interface ConnectorIndicatorHandle {
open : ( ) = > void ;
}
interface ConnectorIndicatorProps {
showTrigger? : boolean ;
}
export const ConnectorIndicator = forwardRef < ConnectorIndicatorHandle , ConnectorIndicatorProps > (
( { showTrigger = true } , ref ) = > {
2026-03-10 23:06:33 +02:00
const searchSpaceId = useAtomValue ( activeSearchSpaceIdAtom ) ;
2026-03-16 21:10:46 +05:30
const setSearchSpaceSettingsDialog = useSetAtom ( searchSpaceSettingsDialogAtom ) ;
2026-03-20 17:09:31 +05:30
useAtomValue ( currentUserAtom ) ;
2026-03-10 23:06:33 +02:00
const { data : preferences = { } , isFetching : preferencesLoading } =
useAtomValue ( llmPreferencesAtom ) ;
const { data : globalConfigs = [ ] , isFetching : globalConfigsLoading } =
useAtomValue ( globalNewLLMConfigsAtom ) ;
2026-02-01 19:56:03 -08:00
2026-03-10 23:06:33 +02:00
// Check if document summary LLM is properly configured
// - If ID is 0 (Auto mode), we need global configs to be available
// - If ID is positive (user config) or negative (specific global config), it's configured
// - If ID is null/undefined, it's not configured
const docSummaryLlmId = preferences . document_summary_llm_id ;
const isAutoMode = docSummaryLlmId === 0 ;
const hasGlobalConfigs = globalConfigs . length > 0 ;
2026-02-01 19:56:03 -08:00
2026-03-10 23:06:33 +02:00
const hasDocumentSummaryLLM =
docSummaryLlmId !== null &&
docSummaryLlmId !== undefined &&
// If it's Auto mode, we need global configs to actually be available
( ! isAutoMode || hasGlobalConfigs ) ;
2026-02-01 19:56:03 -08:00
2026-03-10 23:06:33 +02:00
const llmConfigLoading = preferencesLoading || globalConfigsLoading ;
2026-01-16 11:32:06 -08:00
2026-03-10 23:06:33 +02:00
// Fetch document type counts via the lightweight /type-counts endpoint (cached 10 min)
const { data : documentTypeCounts , isFetching : documentTypesLoading } =
useAtomValue ( documentTypeCountsAtom ) ;
2026-01-01 22:24:42 +05:30
2026-03-10 23:06:33 +02:00
// Read status inbox items from shared atom (populated by LayoutDataProvider)
// instead of creating a duplicate useInbox("status") hook.
const statusInboxItems = useAtomValue ( statusInboxItemsAtom ) ;
const inboxItems = useMemo (
( ) = > statusInboxItems . filter ( ( item ) = > item . type === "connector_indexing" ) ,
[ statusInboxItems ]
) ;
2026-01-23 18:57:10 +05:30
2026-03-10 23:06:33 +02:00
// 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 ,
enableSummary ,
allConnectors ,
viewingAccountsType ,
viewingMCPList ,
2026-03-18 16:08:30 +05:30
isYouTubeView ,
isFromOAuth ,
2026-03-10 23:06:33 +02:00
setSearchQuery ,
setStartDate ,
setEndDate ,
setPeriodicEnabled ,
setFrequencyMinutes ,
setEnableSummary ,
handleOpenChange ,
handleTabChange ,
handleScroll ,
handleConnectOAuth ,
handleConnectNonOAuth ,
handleCreateWebcrawler ,
handleCreateYouTubeCrawler ,
handleSubmitConnectForm ,
handleStartIndexing ,
handleSkipIndexing ,
handleStartEdit ,
handleSaveConnector ,
handleDisconnectConnector ,
handleBackFromEdit ,
handleBackFromConnect ,
handleBackFromYouTube ,
handleViewAccountsList ,
handleBackFromAccountsList ,
handleBackFromMCPList ,
handleAddNewMCPFromList ,
handleQuickIndexConnector ,
connectorConfig ,
setConnectorConfig ,
setIndexingConnectorConfig ,
setConnectorName ,
} = useConnectorDialog ( ) ;
2025-12-30 15:51:10 +05:30
2026-03-10 23:06:33 +02:00
const [ pickerOpen , setPickerOpen ] = useState ( false ) ;
useEffect ( ( ) = > {
const onOpen = ( ) = > setPickerOpen ( true ) ;
const onClose = ( ) = > setPickerOpen ( false ) ;
window . addEventListener ( PICKER_OPEN_EVENT , onOpen ) ;
window . addEventListener ( PICKER_CLOSE_EVENT , onClose ) ;
return ( ) = > {
window . removeEventListener ( PICKER_OPEN_EVENT , onOpen ) ;
window . removeEventListener ( PICKER_CLOSE_EVENT , onClose ) ;
} ;
} , [ ] ) ;
2025-12-31 12:35:43 +05:30
2026-03-10 23:06:33 +02:00
// Fetch connectors using Electric SQL + PGlite for real-time updates
// This provides instant updates when connectors change, without polling
const {
connectors : connectorsFromElectric = [ ] ,
loading : connectorsLoading ,
error : connectorsError ,
refreshConnectors : refreshConnectorsElectric ,
} = useConnectorsElectric ( searchSpaceId ) ;
2025-12-31 12:35:43 +05:30
2026-03-10 23:06:33 +02:00
// Fallback to API if Electric is not available or fails
// Use Electric data if: 1) we have data, or 2) still loading without error
// Use API data if: Electric failed (has error) or finished loading with no data
const useElectricData =
connectorsFromElectric . length > 0 || ( connectorsLoading && ! connectorsError ) ;
const connectors = useElectricData ? connectorsFromElectric : allConnectors || [ ] ;
2025-12-31 12:35:43 +05:30
2026-03-10 23:06:33 +02:00
// Manual refresh function that works with both Electric and API
const refreshConnectors = async ( ) = > {
if ( useElectricData ) {
await refreshConnectorsElectric ( ) ;
} else {
// Fallback: use allConnectors from useConnectorDialog (which uses connectorsAtom)
// The connectorsAtom will handle refetching if needed
}
} ;
2025-12-31 12:35:43 +05:30
2026-03-10 23:06:33 +02:00
// Track indexing state locally - clears automatically when Electric SQL detects last_indexed_at changed
// Also clears when failed notifications are detected
const { indexingConnectorIds , startIndexing , stopIndexing } = useIndexingConnectors (
connectors as SearchSourceConnector [ ] ,
inboxItems
) ;
2025-12-31 12:35:43 +05:30
2026-03-10 23:06:33 +02:00
const isLoading = connectorsLoading || documentTypesLoading ;
2025-12-30 04:03:34 +05:30
2026-03-10 23:06:33 +02:00
// Get document types that have documents in the search space
const activeDocumentTypes = documentTypeCounts
? Object . entries ( documentTypeCounts ) . filter ( ( [ , count ] ) = > count > 0 )
: [ ] ;
2026-01-19 21:23:22 +05:30
2026-03-10 23:06:33 +02:00
const hasConnectors = connectors . length > 0 ;
const hasSources = hasConnectors || activeDocumentTypes . length > 0 ;
const totalSourceCount = connectors . length + activeDocumentTypes . length ;
2025-12-30 04:03:34 +05:30
2026-03-10 23:06:33 +02:00
const activeConnectorsCount = connectors . length ;
2025-12-30 15:51:10 +05:30
2026-03-10 23:06:33 +02:00
// Check which connectors are already connected
// Using Electric SQL + PGlite for real-time connector updates
const connectedTypes = new Set < string > (
( connectors || [ ] ) . map ( ( c : SearchSourceConnector ) = > c . connector_type )
) ;
2026-03-09 23:18:23 -07:00
2026-03-10 23:06:33 +02:00
useImperativeHandle ( ref , ( ) = > ( {
open : ( ) = > handleOpenChange ( true ) ,
} ) ) ;
2025-12-30 04:03:34 +05:30
2026-03-10 23:06:33 +02:00
if ( ! searchSpaceId ) return null ;
2025-12-30 15:51:10 +05:30
2026-03-10 23:06:33 +02:00
return (
2026-03-21 13:20:13 +05:30
< Dialog open = { isOpen } modal = { false } onOpenChange = { handleOpenChange } >
2026-03-10 23:06:33 +02:00
{ showTrigger && (
< TooltipIconButton
data - joyride = "connector-icon"
tooltip = {
hasConnectors ? ` Manage ${ activeConnectorsCount } connectors ` : "Connect your data"
2026-01-01 22:24:42 +05:30
}
2026-03-10 23:06:33 +02:00
side = "bottom"
className = { cn (
"size-[34px] rounded-full p-1 flex items-center justify-center transition-colors relative" ,
"hover:bg-muted-foreground/15 dark:hover:bg-muted-foreground/30" ,
"outline-none focus:outline-none focus-visible:outline-none font-semibold text-xs" ,
"border-0 ring-0 focus:ring-0 shadow-none focus:shadow-none"
) }
aria - label = {
hasConnectors
? ` View ${ activeConnectorsCount } connectors `
: "Add your first connector"
2026-01-01 22:24:42 +05:30
}
2026-03-10 23:06:33 +02:00
onClick = { ( ) = > handleOpenChange ( true ) }
2026-01-01 22:24:42 +05:30
>
2026-03-10 23:06:33 +02:00
{ isLoading ? (
< Spinner size = "sm" / >
) : (
< >
< Cable className = "size-4 stroke-[1.5px]" / >
{ activeConnectorsCount > 0 && (
< span className = "absolute -top-0.5 right-0 flex items-center justify-center min-w-[16px] h-4 px-1 text-[10px] font-medium rounded-full bg-primary text-primary-foreground shadow-sm select-none" >
{ activeConnectorsCount > 99 ? "99+" : activeConnectorsCount }
< / span >
) }
< / >
) }
< / TooltipIconButton >
) }
2026-03-19 17:51:59 +05:30
{ isOpen &&
createPortal (
< div
className = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
aria - hidden = "true"
onClick = { ( ) = > {
if ( ! pickerOpen ) handleOpenChange ( false ) ;
} }
/ > ,
document . body
) }
2026-03-11 15:09:10 -07:00
< DialogContent
onFocusOutside = { ( e ) = > e . preventDefault ( ) }
2026-03-19 17:51:59 +05:30
onInteractOutside = { ( e ) = > {
if ( pickerOpen ) e . preventDefault ( ) ;
} }
onPointerDownOutside = { ( e ) = > {
if ( pickerOpen ) e . preventDefault ( ) ;
} }
2026-03-20 17:09:31 +05:30
className = "max-w-3xl w-[95vw] sm:w-full h-[75vh] sm:h-[85vh] flex flex-col p-0 gap-0 overflow-hidden border border-border ring-0 dark:ring-0 bg-muted dark:bg-muted text-foreground [&>button]:right-4 sm:[&>button]:right-12 [&>button]:top-6 sm:[&>button]:top-10 [&>button]:opacity-80 hover:[&>button]:opacity-100 [&>button_svg]:size-5 select-none"
2026-03-11 15:09:10 -07:00
>
2026-03-10 23:06:33 +02:00
< DialogTitle className = "sr-only" > Manage Connectors < / DialogTitle >
{ /* YouTube Crawler View - shown when adding YouTube videos */ }
{ isYouTubeView && searchSpaceId ? (
< YouTubeCrawlerView searchSpaceId = { searchSpaceId } onBack = { handleBackFromYouTube } / >
) : viewingMCPList ? (
< ConnectorAccountsListView
connectorType = "MCP_CONNECTOR"
connectorTitle = "MCP Connectors"
connectors = { ( allConnectors || [ ] ) as SearchSourceConnector [ ] }
indexingConnectorIds = { indexingConnectorIds }
onBack = { handleBackFromMCPList }
onManage = { handleStartEdit }
onAddAccount = { handleAddNewMCPFromList }
addButtonText = "Add New MCP Server"
/ >
) : viewingAccountsType ? (
< ConnectorAccountsListView
connectorType = { viewingAccountsType . connectorType }
connectorTitle = { viewingAccountsType . connectorTitle }
connectors = { ( connectors || [ ] ) as SearchSourceConnector [ ] } // Using Electric SQL + PGlite for real-time connector updates (all connector types)
indexingConnectorIds = { indexingConnectorIds }
onBack = { handleBackFromAccountsList }
onManage = { handleStartEdit }
onAddAccount = { ( ) = > {
// Check both OAUTH_CONNECTORS and COMPOSIO_CONNECTORS
const oauthConnector =
OAUTH_CONNECTORS . find (
( c ) = > c . connectorType === viewingAccountsType . connectorType
) ||
COMPOSIO_CONNECTORS . find (
( c ) = > c . connectorType === viewingAccountsType . connectorType
) ;
if ( oauthConnector ) {
handleConnectOAuth ( oauthConnector ) ;
}
} }
isConnecting = { connectingId !== null }
2025-12-30 20:07:15 +05:30
/ >
2026-03-10 23:06:33 +02:00
) : connectingConnectorType ? (
< ConnectorConnectView
connectorType = { connectingConnectorType }
onSubmit = { ( formData ) = > handleSubmitConnectForm ( formData , startIndexing ) }
onBack = { handleBackFromConnect }
isSubmitting = { isCreatingConnector }
/ >
) : editingConnector ? (
< ConnectorEditView
connector = { {
. . . editingConnector ,
config : connectorConfig || editingConnector . config ,
name : editingConnector.name ,
// Sync last_indexed_at with live data from Electric SQL for real-time updates
last_indexed_at :
( connectors as SearchSourceConnector [ ] ) . find ( ( c ) = > c . id === editingConnector . id )
? . last_indexed_at ? ? editingConnector . last_indexed_at ,
} }
startDate = { startDate }
endDate = { endDate }
periodicEnabled = { periodicEnabled }
frequencyMinutes = { frequencyMinutes }
enableSummary = { enableSummary }
isSaving = { isSaving }
isDisconnecting = { isDisconnecting }
isIndexing = { indexingConnectorIds . has ( editingConnector . id ) }
searchSpaceId = { searchSpaceId ? . toString ( ) }
onStartDateChange = { setStartDate }
onEndDateChange = { setEndDate }
onPeriodicEnabledChange = { setPeriodicEnabled }
onFrequencyChange = { setFrequencyMinutes }
onEnableSummaryChange = { setEnableSummary }
onSave = { ( ) = > {
startIndexing ( editingConnector . id ) ;
handleSaveConnector ( ( ) = > refreshConnectors ( ) ) ;
} }
onDisconnect = { ( ) = > handleDisconnectConnector ( ( ) = > refreshConnectors ( ) ) }
onBack = { handleBackFromEdit }
2026-03-19 17:51:59 +05:30
onQuickIndex = { ( ( ) = > {
const cfg = connectorConfig || editingConnector . config ;
2026-03-21 13:20:13 +05:30
const isDrive =
editingConnector . connector_type === "GOOGLE_DRIVE_CONNECTOR" ||
2026-03-19 17:51:59 +05:30
editingConnector . connector_type === "COMPOSIO_GOOGLE_DRIVE_CONNECTOR" ;
const hasDriveItems = isDrive
? ( ( cfg ? . selected_folders as unknown [ ] ) ? ? [ ] ) . length > 0 ||
2026-03-21 13:20:13 +05:30
( ( cfg ? . selected_files as unknown [ ] ) ? ? [ ] ) . length > 0
2026-03-19 17:51:59 +05:30
: true ;
if ( ! hasDriveItems ) return undefined ;
return ( ) = > {
startIndexing ( editingConnector . id ) ;
handleQuickIndexConnector (
editingConnector . id ,
editingConnector . connector_type ,
stopIndexing ,
startDate ,
endDate
) ;
} ;
} ) ( ) }
2026-03-10 23:06:33 +02:00
onConfigChange = { setConnectorConfig }
onNameChange = { setConnectorName }
/ >
) : indexingConfig ? (
2026-03-21 13:20:13 +05:30
< IndexingConfigurationView
config = { indexingConfig }
connector = {
indexingConnector
? {
. . . indexingConnector ,
config : indexingConnectorConfig || indexingConnector . config ,
}
: undefined
2026-03-10 23:06:33 +02:00
}
2026-03-21 13:20:13 +05:30
startDate = { startDate }
endDate = { endDate }
periodicEnabled = { periodicEnabled }
frequencyMinutes = { frequencyMinutes }
enableSummary = { enableSummary }
isStartingIndexing = { isStartingIndexing }
isFromOAuth = { isFromOAuth }
onStartDateChange = { setStartDate }
onEndDateChange = { setEndDate }
onPeriodicEnabledChange = { setPeriodicEnabled }
onFrequencyChange = { setFrequencyMinutes }
onEnableSummaryChange = { setEnableSummary }
onConfigChange = { setIndexingConnectorConfig }
onStartIndexing = { ( ) = > {
if ( indexingConfig . connectorId ) {
startIndexing ( indexingConfig . connectorId ) ;
}
handleStartIndexing ( ( ) = > refreshConnectors ( ) ) ;
} }
onSkip = { handleSkipIndexing }
/ >
2026-03-10 23:06:33 +02:00
) : (
< Tabs
value = { activeTab }
onValueChange = { handleTabChange }
className = "flex-1 flex flex-col min-h-0"
>
{ /* Header */ }
< ConnectorDialogHeader
activeTab = { activeTab }
totalSourceCount = { activeConnectorsCount }
searchQuery = { searchQuery }
onTabChange = { handleTabChange }
onSearchChange = { setSearchQuery }
isScrolled = { isScrolled }
/ >
{ /* Content */ }
< div className = "flex-1 min-h-0 relative overflow-hidden" >
< div className = "h-full overflow-y-auto" onScroll = { handleScroll } >
< div className = "px-4 sm:px-12 py-4 sm:py-8 pb-12 sm:pb-16" >
{ /* LLM Configuration Warning */ }
{ ! llmConfigLoading && ! hasDocumentSummaryLLM && (
< Alert variant = "destructive" className = "mb-6" >
< AlertTriangle className = "h-4 w-4" / >
< AlertTitle > LLM Configuration Required < / AlertTitle >
< AlertDescription className = "mt-2" >
< p className = "mb-3" >
{ isAutoMode && ! hasGlobalConfigs
? "Auto mode is selected but no global LLM configurations are available. Please configure a custom LLM in Settings to process and summarize documents from your connected sources."
: "You need to configure a Document Summary LLM before adding connectors. This LLM is used to process and summarize documents from your connected sources." }
< / p >
2026-03-16 21:10:46 +05:30
< Button
2026-03-17 04:40:46 +05:30
size = "sm"
variant = "outline"
onClick = { ( ) = > {
handleOpenChange ( false ) ;
setSearchSpaceSettingsDialog ( {
open : true ,
initialTab : "models" ,
} ) ;
} }
>
< Settings className = "mr-2 h-4 w-4" / >
Go to Settings
< / Button >
2026-03-10 23:06:33 +02:00
< / AlertDescription >
< / Alert >
) }
2025-12-30 20:07:15 +05:30
2026-03-10 23:06:33 +02:00
< TabsContent value = "all" className = "m-0" >
< AllConnectorsTab
searchQuery = { searchQuery }
searchSpaceId = { searchSpaceId }
connectedTypes = { connectedTypes }
connectingId = { connectingId }
allConnectors = { connectors }
documentTypeCounts = { documentTypeCounts }
indexingConnectorIds = { indexingConnectorIds }
onConnectOAuth = { hasDocumentSummaryLLM ? handleConnectOAuth : ( ) = > { } }
onConnectNonOAuth = { hasDocumentSummaryLLM ? handleConnectNonOAuth : ( ) = > { } }
onCreateWebcrawler = {
hasDocumentSummaryLLM ? handleCreateWebcrawler : ( ) = > { }
}
onCreateYouTubeCrawler = {
hasDocumentSummaryLLM ? handleCreateYouTubeCrawler : ( ) = > { }
}
onManage = { handleStartEdit }
onViewAccountsList = { handleViewAccountsList }
/ >
< / TabsContent >
2026-02-01 19:56:03 -08:00
2026-03-10 23:06:33 +02:00
< ActiveConnectorsTab
2026-01-01 22:24:42 +05:30
searchQuery = { searchQuery }
2026-03-10 23:06:33 +02:00
hasSources = { hasSources }
totalSourceCount = { totalSourceCount }
activeDocumentTypes = { activeDocumentTypes }
connectors = { connectors as SearchSourceConnector [ ] }
2026-01-01 22:24:42 +05:30
indexingConnectorIds = { indexingConnectorIds }
2026-03-10 23:06:33 +02:00
onTabChange = { handleTabChange }
2026-01-01 22:24:42 +05:30
onManage = { handleStartEdit }
2026-01-07 09:28:07 +02:00
onViewAccountsList = { handleViewAccountsList }
2026-01-01 22:24:42 +05:30
/ >
2026-03-10 23:06:33 +02:00
< / div >
2025-12-30 16:05:38 +05:30
< / div >
2026-03-10 23:06:33 +02:00
{ /* Bottom fade shadow */ }
< div className = "absolute bottom-0 left-0 right-0 h-7 bg-linear-to-t from-muted via-muted/80 to-transparent pointer-events-none z-10" / >
2025-12-30 15:51:10 +05:30
< / div >
2026-03-10 23:06:33 +02:00
< / Tabs >
) }
< / DialogContent >
< / Dialog >
) ;
}
) ;
2026-03-09 23:18:23 -07:00
ConnectorIndicator . displayName = "ConnectorIndicator" ;