'use client'
import type { ReactNode } from 'react'
import type { NewKnowledgeSourceDraft, NewKnowledgeSourceType } from './routes'
import type { InstalledSourceProviderOption, SourceProviderOption } from './source-provider-options'
import { Button, buttonVariants } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
import { Input } from '@langgenius/dify-ui/input'
import { RadioGroup, RadioItem } from '@langgenius/dify-ui/radio'
import { useTranslation } from 'react-i18next'
import { buildIntegrationPath } from '@/app/components/integrations/routes'
import Link from '@/next/link'
import { NEW_KNOWLEDGE_SOURCE_NAME_MAX_LENGTH } from './routes'
import { SyncPolicyField } from './sync-policy-field'
const sourceTypeOptions = [
{ icon: 'i-ri-global-line', iconSize: 'size-4', value: 'websiteCrawl' },
{ icon: 'i-ri-file-text-line', iconSize: 'size-3.5', value: 'onlineDocuments' },
{ icon: 'i-ri-hard-drive-3-line', iconSize: 'size-3.5', value: 'onlineDrive' },
] as const
const defaultSyncPolicies = ['provider', 'daily', 'manual'] as const
export function SourceTypeSelector({
appearance = 'page',
disabled = false,
disabledValues = [],
value,
onChange,
}: {
appearance?: 'embedded' | 'page'
disabled?: boolean
disabledValues?: readonly NewKnowledgeSourceType[]
value: NewKnowledgeSourceType
onChange: (value: NewKnowledgeSourceType) => void
}) {
const { t } = useTranslation('dataset')
return (
)
}
export function SourceProviderRadioGroup({
disabled = false,
layout,
options,
size = 'medium',
surface = 'transparent',
value,
onChange,
}: {
disabled?: boolean
layout: 'grid-four' | 'grid-three' | 'wrap'
options: Array<{ disabled?: boolean; icon: ReactNode; label?: ReactNode; value: T }>
size?: 'medium' | 'small'
surface?: 'default' | 'transparent'
value: T
onChange: (value: T) => void
}) {
return (
value={value}
disabled={disabled}
className={cn(
layout === 'wrap' ? 'flex flex-wrap gap-2' : 'grid grid-cols-2 gap-2',
layout === 'grid-three' && 'sm:grid-cols-3',
layout === 'grid-four' && 'sm:grid-cols-4',
)}
onValueChange={onChange}
>
{options.map((option) => (
key={option.value}
value={option.value}
disabled={disabled || option.disabled}
className={cn(
'relative flex items-center justify-center gap-1.5 rounded-lg border border-divider-subtle system-xs-medium text-text-secondary outline-hidden',
size === 'medium' ? 'h-8.5 px-2.5' : 'h-7.5 px-3',
surface === 'default' && 'bg-background-default',
'hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid',
'data-checked:border-[1.5px] data-checked:border-components-option-card-option-selected-border data-checked:bg-components-option-card-option-selected-bg data-checked:text-text-primary',
'data-disabled:cursor-not-allowed data-disabled:opacity-60',
)}
>
{option.icon}
{option.label ?? option.value}
))}
)
}
type SourceProviderIconValue =
| InstalledSourceProviderOption['datasource']['identity']['icon']
| InstalledSourceProviderOption['plugin']['declaration']['identity']['icon']
export function SourceProviderIcon({
className,
fallbackIcon,
icon,
}: {
className?: string
fallbackIcon: string
icon?: SourceProviderIconValue
}) {
if (typeof icon === 'string' && icon)
return (
)
if (icon && typeof icon !== 'string')
return (
{icon.content}
)
return
}
export function SourceProviderSelector({
appearance = 'page',
disabled = false,
layout = 'grid-three',
options,
providerKey,
onChange,
}: {
appearance?: 'embedded' | 'page'
disabled?: boolean
layout?: 'grid-four' | 'grid-three'
options: SourceProviderOption[]
providerKey: string
onChange: (providerKey: string) => void
}) {
const { t } = useTranslation('dataset')
return (
)
}
export function SourceNameField({
className,
disabled = false,
draft,
labelClassName,
name = 'sourceName',
preventSubmitOnEnter = false,
onDraftChange,
}: {
className?: string
disabled?: boolean
draft: NewKnowledgeSourceDraft
labelClassName?: string
name?: string
preventSubmitOnEnter?: boolean
onDraftChange: (draft: NewKnowledgeSourceDraft) => void
}) {
const { t } = useTranslation('dataset')
return (
{t(($) => $['newKnowledge.sourceName'])}
*
$['newKnowledge.sourceNamePlaceholder'])}
onValueChange={(value) => onDraftChange({ ...draft, sourceName: value })}
onKeyDown={(event) => {
if (preventSubmitOnEnter && event.key === 'Enter') event.preventDefault()
}}
/>
)
}
export function SourceSyncPolicyField({
availablePolicies = defaultSyncPolicies,
className,
disabled = false,
draft,
size,
triggerClassName,
onDraftChange,
}: {
availablePolicies?: readonly NewKnowledgeSourceDraft['syncPolicy'][]
className?: string
disabled?: boolean
draft: NewKnowledgeSourceDraft
size?: 'large' | 'medium'
triggerClassName?: string
onDraftChange: (draft: NewKnowledgeSourceDraft) => void
}) {
return (
onDraftChange({
...draft,
customIntervalSeconds: value.customIntervalSeconds,
syncPolicy: value.mode === 'interval' ? 'daily' : value.mode,
})
}
/>
)
}
export function SourceConnectionRequiredCard({
actionLabel,
description,
disabled = false,
icon,
title,
onConnect,
}: {
actionLabel: string
description: string
disabled?: boolean
icon: ReactNode
title: string
onConnect: () => void
}) {
return (
{icon}
{title}
{description}
)
}
export function SourceProviderCredentialRequiredCard({
disabled = false,
icon,
provider,
onConnect,
}: {
disabled?: boolean
icon: ReactNode
provider: string
onConnect: () => void
}) {
const { t } = useTranslation('dataset')
return (
$['newKnowledge.connectProvider'], { provider })}
description={t(($) => $['newKnowledge.providerCredentialRequiredDescription'], {
provider,
})}
disabled={disabled}
icon={icon}
title={t(($) => $['newKnowledge.providerNotConfigured'], { provider })}
onConnect={onConnect}
/>
)
}
export function SourceProviderNotInstalledCard({
icon,
provider,
onInstall,
}: {
icon: ReactNode
provider: string
onInstall: () => void
}) {
const { t: tPlugin } = useTranslation('plugin')
const { t: tWorkflow } = useTranslation('workflow')
return (
{icon}
{provider}
{tWorkflow(($) => $['nodes.common.pluginNotInstalled'])}
)
}