Files
FastGPT/projects/app/src/pageComponents/dashboard/SystemPlugin/ToolCard.tsx
Ctrlz 31c12fdeb9 Enhance file upload functionality and system tool integration (#5257)
* Enhance file upload functionality and system tool integration

* Add supplementary documents and optimize the upload interface

* Refactor file plugin types and update upload configurations

* Refactor MinIO configuration variables and clean up API plugin handlers for improved readability and consistency

* File name change

* Refactor SystemTools component layout

* fix i18n

* fix

* fix

* fix
2025-07-31 11:46:10 +08:00

157 lines
4.6 KiB
TypeScript

import { useSystemStore } from '@/web/common/system/useSystemStore';
import { Box, Flex, HStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyBox from '@fastgpt/web/components/common/MyBox';
import React, { useState } from 'react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { type NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node';
import { type PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
const PluginCard = ({
item,
groups,
onDelete
}: {
item: NodeTemplateListItemType;
groups: PluginGroupSchemaType[];
onDelete?: (pluginId: string) => Promise<void>;
}) => {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const [isHovered, setIsHovered] = useState(false);
const { openConfirm, ConfirmModal } = useConfirm({
type: 'delete',
content: t('common:sure_delete_tool_cannot_undo')
});
const type = groups.reduce<string | undefined>((acc, group) => {
const foundType = group.groupTypes.find((type) => type.typeId === item.templateType);
return foundType ? foundType.typeName : acc;
}, undefined);
const isUploadedPlugin = item.toolSource === 'uploaded';
const handleDelete = async () => {
if (onDelete && item.id) {
await onDelete(item.id);
}
};
const handleDeleteClick = () => {
openConfirm(handleDelete)();
};
return (
<MyBox
key={item.id}
lineHeight={1.5}
h="100%"
pt={4}
pb={3}
px={4}
border={'base'}
boxShadow={'2'}
bg={'white'}
borderRadius={'10px'}
position={'relative'}
display={'flex'}
flexDirection={'column'}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
_hover={{
borderColor: 'primary.300',
boxShadow: '1.5'
}}
>
{/* Delete button with centered confirmation modal */}
{isUploadedPlugin && (
<MyIconButton
icon="delete"
position="absolute"
bottom={3}
right={4}
color="blue.500"
aria-label={t('common:Delete')}
zIndex={1}
opacity={isHovered ? 1 : 0}
pointerEvents={isHovered ? 'auto' : 'none'}
onClick={handleDeleteClick}
/>
)}
<ConfirmModal />
<HStack>
<Avatar src={item.avatar} borderRadius={'sm'} w={'1.5rem'} h={'1.5rem'} />
<Box flex={'1 0 0'} color={'myGray.900'} fontWeight={500}>
{item.name}
</Box>
<Box mr={'-1rem'}>
<Flex
bg={'myGray.100'}
color={'myGray.600'}
py={0.5}
pl={2}
pr={3}
borderLeftRadius={'sm'}
whiteSpace={'nowrap'}
>
<Box ml={1} fontSize={'mini'}>
{t(type as any)}
</Box>
</Flex>
</Box>
</HStack>
<Box
flex={['1 0 48px', '1 0 56px']}
mt={3}
pr={1}
textAlign={'justify'}
wordBreak={'break-all'}
fontSize={'xs'}
color={'myGray.500'}
>
<Box className={'textEllipsis2'}>{item.intro || t('app:templateMarket.no_intro')}</Box>
</Box>
<Flex w={'full'} fontSize={'mini'}>
<Flex flex={1}>
{(item.instructions || item.courseUrl) && (
<UseGuideModal
title={item.name}
iconSrc={item.avatar}
text={item.instructions}
link={item.courseUrl}
>
{({ onClick }) => (
<Flex
color={'primary.700'}
alignItems={'center'}
gap={1}
cursor={'pointer'}
onClick={onClick}
_hover={{ bg: 'myGray.100' }}
>
<MyIcon name={'book'} w={'14px'} />
{t('app:plugin.Instructions')}
</Flex>
)}
</UseGuideModal>
)}
</Flex>
{/* Hide author info when showing delete button but maintain space */}
<Box color={'myGray.500'} visibility={isUploadedPlugin && isHovered ? 'hidden' : 'visible'}>
{`by ${item.author || feConfigs.systemTitle}`}
</Box>
</Flex>
</MyBox>
);
};
export default React.memo(PluginCard);