Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useCallback, useMemo, useState } from 'react'
import { useMemo, useState } from 'react'
import { ArrowLeft, ArrowLeftRight, Loader2, Plus, Search } from 'lucide-react'
import { useParams } from 'next/navigation'
import {
Expand All @@ -23,11 +23,11 @@ import { getSubscriptionAccessState } from '@/lib/billing/client'
import { consumeOAuthReturnContext } from '@/lib/credentials/client-state'
import { getProviderIdFromServiceId, type OAuthProvider } from '@/lib/oauth'
import { OAuthModal } from '@/app/workspace/[workspaceId]/components/oauth-modal'
import { ConnectorSelectorField } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/components/connector-selector-field'
import { ConnectorSelectorField } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-selector-field'
import { SYNC_INTERVALS } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/consts'
import { MaxBadge } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/max-badge'
import { useConnectorConfigFields } from '@/app/workspace/[workspaceId]/knowledge/[id]/hooks/use-connector-config-fields'
import { isBillingEnabled } from '@/app/workspace/[workspaceId]/settings/navigation'
import { getDependsOnFields } from '@/blocks/utils'
import { CONNECTOR_REGISTRY } from '@/connectors/registry'
import type { ConnectorConfig, ConnectorConfigField } from '@/connectors/types'
import { useCreateConnector } from '@/hooks/queries/kb/connectors'
Expand Down Expand Up @@ -57,13 +57,11 @@ export function AddConnectorModal({
}: AddConnectorModalProps) {
const [step, setStep] = useState<Step>(() => (initialConnectorType ? 'configure' : 'select-type'))
const [selectedType, setSelectedType] = useState<string | null>(initialConnectorType ?? null)
const [sourceConfig, setSourceConfig] = useState<Record<string, string>>({})
const [syncInterval, setSyncInterval] = useState(1440)
const [selectedCredentialId, setSelectedCredentialId] = useState<string | null>(null)
const [disabledTagIds, setDisabledTagIds] = useState<Set<string>>(() => new Set())
const [error, setError] = useState<string | null>(null)
const [showOAuthModal, setShowOAuthModal] = useState(false)
const [canonicalModes, setCanonicalModes] = useState<Record<string, 'basic' | 'advanced'>>({})

const [apiKeyValue, setApiKeyValue] = useState('')
const [apiKeyFocused, setApiKeyFocused] = useState(false)
Expand Down Expand Up @@ -100,54 +98,17 @@ export function AddConnectorModal({
const effectiveCredentialId =
selectedCredentialId ?? (credentials.length === 1 ? credentials[0].id : null)

const canonicalGroups = useMemo(() => {
if (!connectorConfig) return new Map<string, ConnectorConfigField[]>()
const groups = new Map<string, ConnectorConfigField[]>()
for (const field of connectorConfig.configFields) {
if (field.canonicalParamId) {
const existing = groups.get(field.canonicalParamId)
if (existing) {
existing.push(field)
} else {
groups.set(field.canonicalParamId, [field])
}
}
}
return groups
}, [connectorConfig])

const dependentFieldIds = useMemo(() => {
if (!connectorConfig) return new Map<string, string[]>()
const map = new Map<string, string[]>()
for (const field of connectorConfig.configFields) {
const deps = getDependsOnFields(field.dependsOn)
for (const dep of deps) {
const existing = map.get(dep) ?? []
existing.push(field.id)
map.set(dep, existing)
}
}
for (const group of canonicalGroups.values()) {
const allDependents = new Set<string>()
for (const field of group) {
for (const dep of map.get(field.id) ?? []) {
allDependents.add(dep)
const depField = connectorConfig.configFields.find((f) => f.id === dep)
if (depField?.canonicalParamId) {
for (const sibling of canonicalGroups.get(depField.canonicalParamId) ?? []) {
allDependents.add(sibling.id)
}
}
}
}
if (allDependents.size > 0) {
for (const field of group) {
map.set(field.id, [...allDependents])
}
}
}
return map
}, [connectorConfig, canonicalGroups])
const {
sourceConfig,
setSourceConfig,
canonicalModes,
setCanonicalModes,
canonicalGroups,
isFieldVisible,
handleFieldChange,
toggleCanonicalMode,
resolveSourceConfig,
} = useConnectorConfigFields({ connectorConfig })

const handleSelectType = (type: string) => {
setSelectedType(type)
Expand All @@ -163,64 +124,6 @@ export function AddConnectorModal({
onConnectorTypeChange?.(type)
}

const handleFieldChange = useCallback(
(fieldId: string, value: string) => {
setSourceConfig((prev) => {
const next = { ...prev, [fieldId]: value }
const toClear = dependentFieldIds.get(fieldId)
if (toClear) {
for (const depId of toClear) {
next[depId] = ''
}
}
return next
})
},
[dependentFieldIds]
)

const toggleCanonicalMode = useCallback((canonicalId: string) => {
setCanonicalModes((prev) => ({
...prev,
[canonicalId]: prev[canonicalId] === 'advanced' ? 'basic' : 'advanced',
}))
}, [])

const isFieldVisible = useCallback(
(field: ConnectorConfigField): boolean => {
if (!field.canonicalParamId || !field.mode) return true
const activeMode = canonicalModes[field.canonicalParamId] ?? 'basic'
return field.mode === activeMode
},
[canonicalModes]
)

const resolveSourceConfig = useCallback((): Record<string, string> => {
const resolved: Record<string, string> = {}
const processedCanonicals = new Set<string>()

if (!connectorConfig) return resolved

for (const field of connectorConfig.configFields) {
if (field.canonicalParamId) {
if (processedCanonicals.has(field.canonicalParamId)) continue
processedCanonicals.add(field.canonicalParamId)

const group = canonicalGroups.get(field.canonicalParamId)
if (!group) continue

const activeMode = canonicalModes[field.canonicalParamId] ?? 'basic'
const activeField = group.find((f) => f.mode === activeMode) ?? group[0]
const value = sourceConfig[activeField.id]
if (value) resolved[field.canonicalParamId] = value
} else {
if (sourceConfig[field.id]) resolved[field.id] = sourceConfig[field.id]
}
}

return resolved
}, [connectorConfig, canonicalGroups, canonicalModes, sourceConfig])

const canSubmit = useMemo(() => {
if (!connectorConfig) return false
if (isApiKeyMode) {
Expand Down Expand Up @@ -249,7 +152,10 @@ export function AddConnectorModal({

setError(null)

const resolvedConfig = resolveSourceConfig()
const resolvedConfig: Record<string, unknown> = {}
for (const [key, value] of Object.entries(resolveSourceConfig())) {
if (value) resolvedConfig[key] = value
}
const finalSourceConfig =
disabledTagIds.size > 0
? { ...resolvedConfig, disabledTagIds: Array.from(disabledTagIds) }
Expand All @@ -274,10 +180,6 @@ export function AddConnectorModal({
)
}

const handleConnectNewAccount = useCallback(() => {
setShowOAuthModal(true)
}, [])

const filteredEntries = useMemo(() => {
const term = searchTerm.toLowerCase().trim()
if (!term) return CONNECTOR_ENTRIES
Expand Down Expand Up @@ -385,9 +287,7 @@ export function AddConnectorModal({
: `Connect ${connectorConfig.name} account`,
value: '__connect_new__',
icon: Plus,
onSelect: () => {
void handleConnectNewAccount()
},
onSelect: () => setShowOAuthModal(true),
},
]}
value={effectiveCredentialId ?? undefined}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ConnectorSelectorField } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-selector-field/connector-selector-field'
Loading
Loading