| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- 'use client'
- import { useCallback, useState } from 'react'
- import { useBoolean } from 'ahooks'
- import { useTranslation } from 'react-i18next'
- import { useAppContext } from '@/context/app-context'
- import { RiHammerFill } from '@remixicon/react'
- import Indicator from '@/app/components/header/indicator'
- import Icon from '@/app/components/plugins/card/base/card-icon'
- import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
- import type { ToolWithProvider } from '../../workflow/types'
- import Confirm from '@/app/components/base/confirm'
- import MCPModal from './modal'
- import OperationDropdown from './detail/operation-dropdown'
- import { useDeleteMCP, useUpdateMCP } from '@/service/use-tools'
- import cn from '@/utils/classnames'
-
- type Props = {
- currentProvider?: ToolWithProvider
- data: ToolWithProvider
- handleSelect: (providerID: string) => void
- onUpdate: (providerID: string) => void
- onDeleted: () => void
- }
-
- const MCPCard = ({
- currentProvider,
- data,
- onUpdate,
- handleSelect,
- onDeleted,
- }: Props) => {
- const { t } = useTranslation()
- const { formatTimeFromNow } = useFormatTimeFromNow()
- const { isCurrentWorkspaceManager } = useAppContext()
-
- const { mutateAsync: updateMCP } = useUpdateMCP({})
- const { mutateAsync: deleteMCP } = useDeleteMCP({})
-
- const [isOperationShow, setIsOperationShow] = useState(false)
-
- const [isShowUpdateModal, {
- setTrue: showUpdateModal,
- setFalse: hideUpdateModal,
- }] = useBoolean(false)
-
- const [isShowDeleteConfirm, {
- setTrue: showDeleteConfirm,
- setFalse: hideDeleteConfirm,
- }] = useBoolean(false)
-
- const [deleting, {
- setTrue: showDeleting,
- setFalse: hideDeleting,
- }] = useBoolean(false)
-
- const handleUpdate = useCallback(async (form: any) => {
- const res = await updateMCP({
- ...form,
- provider_id: data.id,
- })
- if ((res as any)?.result === 'success') {
- hideUpdateModal()
- onUpdate(data.id)
- }
- }, [data, updateMCP, hideUpdateModal, onUpdate])
-
- const handleDelete = useCallback(async () => {
- showDeleting()
- const res = await deleteMCP(data.id)
- hideDeleting()
- if ((res as any)?.result === 'success') {
- hideDeleteConfirm()
- onDeleted()
- }
- }, [showDeleting, deleteMCP, data.id, hideDeleting, hideDeleteConfirm, onDeleted])
-
- return (
- <div
- onClick={() => handleSelect(data.id)}
- className={cn(
- 'group relative flex cursor-pointer flex-col rounded-xl border-[1.5px] border-transparent bg-components-card-bg shadow-xs hover:bg-components-card-bg-alt hover:shadow-md',
- currentProvider?.id === data.id && 'border-components-option-card-option-selected-border bg-components-card-bg-alt',
- )}
- >
- <div className='flex grow items-center gap-3 rounded-t-xl p-4'>
- <div className='shrink-0 overflow-hidden rounded-xl border border-components-panel-border-subtle'>
- <Icon src={data.icon} />
- </div>
- <div className='grow'>
- <div className='system-md-semibold mb-1 truncate text-text-secondary' title={data.name}>{data.name}</div>
- <div className='system-xs-regular text-text-tertiary'>{data.server_identifier}</div>
- </div>
- </div>
- <div className='flex items-center gap-1 rounded-b-xl pb-2.5 pl-4 pr-2.5 pt-1.5'>
- <div className='flex w-0 grow items-center gap-2'>
- <div className='flex items-center gap-1'>
- <RiHammerFill className='h-3 w-3 shrink-0 text-text-quaternary' />
- {data.tools.length > 0 && (
- <div className='system-xs-regular shrink-0 text-text-tertiary'>{t('tools.mcp.toolsCount', { count: data.tools.length })}</div>
- )}
- {!data.tools.length && (
- <div className='system-xs-regular shrink-0 text-text-tertiary'>{t('tools.mcp.noTools')}</div>
- )}
- </div>
- <div className={cn('system-xs-regular text-divider-deep', (!data.is_team_authorization || !data.tools.length) && 'sm:hidden')}>/</div>
- <div className={cn('system-xs-regular truncate text-text-tertiary', (!data.is_team_authorization || !data.tools.length) && ' sm:hidden')} title={`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}>{`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}</div>
- </div>
- {data.is_team_authorization && data.tools.length > 0 && <Indicator color='green' className='shrink-0' />}
- {(!data.is_team_authorization || !data.tools.length) && (
- <div className='system-xs-medium flex shrink-0 items-center gap-1 rounded-md border border-util-colors-red-red-500 bg-components-badge-bg-red-soft px-1.5 py-0.5 text-util-colors-red-red-500'>
- {t('tools.mcp.noConfigured')}
- <Indicator color='red' />
- </div>
- )}
- </div>
- {isCurrentWorkspaceManager && (
- <div className={cn('absolute right-2.5 top-2.5 hidden group-hover:block', isOperationShow && 'block')} onClick={e => e.stopPropagation()}>
- <OperationDropdown
- inCard
- onOpenChange={setIsOperationShow}
- onEdit={showUpdateModal}
- onRemove={showDeleteConfirm}
- />
- </div>
- )}
- {isShowUpdateModal && (
- <MCPModal
- data={data}
- show={isShowUpdateModal}
- onConfirm={handleUpdate}
- onHide={hideUpdateModal}
- />
- )}
- {isShowDeleteConfirm && (
- <Confirm
- isShow
- title={t('tools.mcp.delete')}
- content={
- <div>
- {t('tools.mcp.deleteConfirmTitle', { mcp: data.name })}
- </div>
- }
- onCancel={hideDeleteConfirm}
- onConfirm={handleDelete}
- isLoading={deleting}
- isDisabled={deleting}
- />
- )}
- </div>
- )
- }
- export default MCPCard
|