dify/web/app/components/tools/mcp/index.tsx
Jingyi 9b74df21d0
feat(web): refine onboarding UI (#37433)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
Co-authored-by: gigglewang <gigglewang@dify.ai>
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
Co-authored-by: chariri <w@chariri.moe>
Co-authored-by: Evan <2869018789@qq.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-06-15 08:47:15 +00:00

126 lines
3.8 KiB
TypeScript

'use client'
import type { ToolsContentInset } from '../content-inset'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
import { useEffect, useMemo, useState } from 'react'
import ToolCardSkeletonGrid from '@/app/components/tools/provider/tool-card-skeleton'
import {
useAllToolProviders,
} from '@/service/use-tools'
import { toolsContentInsetClassNames, toolsUnifiedContentFrameClassName } from '../content-inset'
import NewMCPCard from './create-card'
import MCPDetailPanel from './detail/provider-detail'
import MCPCard from './provider-card'
type Props = Readonly<{
searchText: string
contentInset?: ToolsContentInset
createdProviderId?: string
onCreatedProviderHandled?: () => void
showCreateCard?: boolean
}>
const MCPList = ({
searchText,
contentInset = 'default',
createdProviderId,
onCreatedProviderHandled,
showCreateCard = true,
}: Props) => {
const { data: list = [] as ToolWithProvider[], isLoading, refetch } = useAllToolProviders()
const [isTriggerAuthorize, setIsTriggerAuthorize] = useState<boolean>(false)
const filteredList = useMemo(() => {
return list.filter((collection) => {
if (collection.type !== 'mcp')
return false
if (searchText)
return Object.values(collection.name).some(value => (value as string).toLowerCase().includes(searchText.toLowerCase()))
return true
}) as ToolWithProvider[]
}, [list, searchText])
const [currentProviderID, setCurrentProviderID] = useState<string>()
const currentProvider = useMemo(() => {
return list.find(provider => provider.id === currentProviderID)
}, [list, currentProviderID])
const handleCreate = async (provider: ToolWithProvider) => {
await refetch() // update list
setCurrentProviderID(provider.id)
setIsTriggerAuthorize(true)
}
useEffect(() => {
if (!createdProviderId)
return
let isActive = true
const openCreatedProvider = async () => {
try {
await refetch()
if (!isActive)
return
setCurrentProviderID(createdProviderId)
setIsTriggerAuthorize(true)
}
finally {
if (isActive)
onCreatedProviderHandled?.()
}
}
void openCreatedProvider()
return () => {
isActive = false
}
}, [createdProviderId, onCreatedProviderHandled, refetch])
const handleUpdate = async (providerID: string) => {
await refetch() // update list
setCurrentProviderID(providerID)
setIsTriggerAuthorize(true)
}
const contentPaddingClassName = toolsContentInsetClassNames[contentInset]
const contentFrameClassName = cn(contentPaddingClassName, toolsUnifiedContentFrameClassName)
return (
<>
<div
className={cn(
'relative grid shrink-0 grid-cols-1 content-start gap-4 pt-2 pb-4 sm:grid-cols-2 md:grid-cols-3',
contentFrameClassName,
isLoading && 'h-[calc(100vh-136px)] overflow-hidden',
)}
>
{!isLoading && showCreateCard && <NewMCPCard handleCreate={handleCreate} />}
{isLoading
? <ToolCardSkeletonGrid variant="mcp" />
: filteredList.map(provider => (
<MCPCard
key={provider.id}
data={provider}
currentProvider={currentProvider as ToolWithProvider}
handleSelect={setCurrentProviderID}
onUpdate={handleUpdate}
onDeleted={refetch}
/>
))}
</div>
{currentProvider && (
<MCPDetailPanel
detail={currentProvider as ToolWithProvider}
onHide={() => setCurrentProviderID(undefined)}
onUpdate={refetch}
isTriggerAuthorize={isTriggerAuthorize}
onFirstCreate={() => setIsTriggerAuthorize(false)}
/>
)}
</>
)
}
export default MCPList