-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(marketplace): update plugin/ download count statistic #5957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,12 +7,6 @@ import MyIcon from '../../../common/Icon'; | |
| import { parseI18nString } from '@fastgpt/global/common/i18n/utils'; | ||
| import { PluginStatusEnum } from '@fastgpt/global/core/plugin/type'; | ||
|
|
||
| /* | ||
| 3 种使用场景: | ||
| 1. admin 视角插件市场:显示是否安装,无状态,显示安装/卸载 | ||
| 2. team 视角资源库:显示是否安装,状态文本,以及安装/卸载 | ||
| 3. 开放的插件市场:不显示任何状态,只显示下载按钮 | ||
| */ | ||
| export type ToolCardItemType = { | ||
| id: string; | ||
| name: string; | ||
|
|
@@ -23,21 +17,35 @@ export type ToolCardItemType = { | |
| downloadUrl?: string; | ||
| status?: number; | ||
| installed?: boolean; | ||
| update?: boolean; | ||
| downloadCount?: number; | ||
| }; | ||
|
|
||
| /** | ||
| 3 种使用场景: | ||
| 1. admin 视角插件市场:显示是否安装,是否更新,无状态,显示安装/卸载 | ||
| 2. team 视角资源库:显示是否安装,不显示更新,状态文本,以及安装/卸载 | ||
| 3. 开放的插件市场:不显示任何状态,只显示下载按钮 | ||
| */ | ||
| const ToolCard = ({ | ||
| item, | ||
| systemTitle, | ||
| isLoading, | ||
| isInstallingOrDeleting, | ||
| isUpdating, | ||
| mode, | ||
| onClickButton, | ||
| onInstall, | ||
| onDelete, | ||
| onUpdate, | ||
| onClickCard | ||
| }: { | ||
| item: ToolCardItemType; | ||
| systemTitle?: string; | ||
| isLoading?: boolean; | ||
| isInstallingOrDeleting?: boolean; | ||
| isUpdating?: boolean; | ||
| mode: 'admin' | 'team' | 'marketplace'; | ||
| onClickButton: (installed: boolean) => void; | ||
| onInstall: () => Promise<void>; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不会都有 install 把? |
||
| onDelete?: () => Promise<void>; | ||
| onUpdate?: () => Promise<void>; | ||
|
Comment on lines
+46
to
+48
|
||
| onClickCard?: () => void; | ||
| }) => { | ||
| const { t, i18n } = useTranslation(); | ||
|
|
@@ -76,7 +84,7 @@ const ToolCard = ({ | |
| }; | ||
| }, [item.tags]); | ||
|
|
||
| const statusMap = useMemo(() => { | ||
| const statusLabel = useMemo(() => { | ||
| if (mode === 'marketplace') return null; | ||
|
|
||
| const pluginStatusMap: Record<number, { label: string; color: string; icon?: string } | null> = | ||
|
|
@@ -91,23 +99,27 @@ const ToolCard = ({ | |
| } | ||
| }; | ||
|
|
||
| const installedStatusMap = item.installed | ||
| ? { | ||
| label: t('app:toolkit_installed'), | ||
| color: 'myGray.500', | ||
| icon: 'common/check' | ||
| } | ||
| : null; | ||
|
|
||
| if (mode === 'admin') { | ||
| return installedStatusMap; | ||
| return item.installed | ||
| ? { | ||
| label: t('app:toolkit_installed'), | ||
| color: 'myGray.500', | ||
| icon: 'common/check' | ||
| } | ||
| : null; | ||
| } | ||
|
|
||
| if (mode === 'team') { | ||
| if (item.status && pluginStatusMap[item.status]) { | ||
| return pluginStatusMap[item.status]; | ||
| } | ||
| return installedStatusMap; | ||
| return item.installed | ||
| ? { | ||
| label: t('app:toolkit_installed'), | ||
| color: 'myGray.500', | ||
| icon: 'common/check' | ||
| } | ||
| : null; | ||
| } | ||
| }, [item.installed, item.status]); | ||
|
|
||
|
|
@@ -122,23 +134,77 @@ const ToolCard = ({ | |
| display={'flex'} | ||
| flexDirection={'column'} | ||
| cursor={onClickCard ? 'pointer' : 'default'} | ||
| onClick={onClickCard} | ||
| position={'relative'} | ||
| onClick={() => { | ||
| if (isInstallingOrDeleting || isUpdating) return; | ||
| onClickCard?.(); | ||
| }} | ||
| _hover={{ | ||
| boxShadow: '0 4px 4px 0 rgba(19, 51, 107, 0.05), 0 0 1px 0 rgba(19, 51, 107, 0.08);', | ||
| '& .install-button': { | ||
| display: 'flex' | ||
| }, | ||
| '& .update-button': { | ||
| display: 'flex' | ||
| }, | ||
| // Only hide author info when there are multiple buttons | ||
| ...(item.update && mode === 'admin' | ||
| ? { | ||
| '& .author-info': { | ||
| display: 'none' | ||
| } | ||
| } | ||
| : {}), | ||
| '& .download-count': { | ||
| display: 'none' | ||
| } | ||
| }} | ||
| > | ||
| {/* Update badge in top-right corner */} | ||
| {item.update && mode === 'admin' && ( | ||
| <Flex | ||
| alignItems="center" | ||
| position={'absolute'} | ||
| top={4} | ||
| right={4} | ||
| px={2} | ||
| py={0.5} | ||
| bg={'rgb(255, 247, 237)'} | ||
| color={'rgba(234,88,12,1)'} | ||
| fontSize={'12px'} | ||
| fontWeight={'medium'} | ||
| borderRadius={'0.5rem'} | ||
| borderColor={'rgba(255,237,213,1)'} | ||
| borderWidth={'1px'} | ||
| zIndex={1} | ||
| > | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="12" | ||
| height="12" | ||
| viewBox="0 0 24 24" | ||
| fill="currentColor" | ||
| stroke="currentColor" | ||
| strokeWidth="2" | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| aria-hidden="true" | ||
| > | ||
| <path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"></path> | ||
| </svg> | ||
| {t('app:app.modules.has new version')} | ||
| </Flex> | ||
| )} | ||
|
|
||
| <HStack> | ||
| <Avatar src={item.icon} borderRadius={'sm'} w={'1.5rem'} /> | ||
| <Box color={'myGray.900'} fontWeight={'medium'}> | ||
| {parseI18nString(item.name, i18n.language)} | ||
| </Box> | ||
| {statusMap && ( | ||
| <Flex fontSize={'12px'} fontWeight={'medium'} color={statusMap.color} gap={1}> | ||
| {statusMap.icon && <MyIcon name={statusMap.icon as any} w={4} />} | ||
| {statusMap.label} | ||
| {statusLabel && ( | ||
| <Flex fontSize={'12px'} fontWeight={'medium'} color={statusLabel.color} gap={1}> | ||
| {statusLabel.icon && <MyIcon name={statusLabel.icon as any} w={4} />} | ||
| {statusLabel.label} | ||
| </Flex> | ||
| )} | ||
| </HStack> | ||
|
|
@@ -194,36 +260,77 @@ const ToolCard = ({ | |
| </Flex> | ||
|
|
||
| <Flex w={'full'} fontSize={'mini'} alignItems={'end'} justifyContent={'space-between'}> | ||
| <Box color={'myGray.500'} mt={3}>{`by ${item.author || systemTitle || 'FastGPT'}`}</Box> | ||
| {mode === 'marketplace' ? ( | ||
| <Button | ||
| className="install-button" | ||
| size={'sm'} | ||
| variant={'primary'} | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| onClickButton(false); | ||
| }} | ||
| isLoading={isLoading} | ||
| {...(!isLoading ? { display: 'none' } : {})} | ||
| > | ||
| {t('common:Download')} | ||
| </Button> | ||
| ) : ( | ||
| <Button | ||
| className="install-button" | ||
| {...(!isLoading ? { display: 'none' } : {})} | ||
| size={'sm'} | ||
| variant={item.installed ? 'primaryOutline' : 'primary'} | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| onClickButton(!item.installed); | ||
| }} | ||
| isLoading={isLoading} | ||
| > | ||
| {item.installed ? t('app:toolkit_uninstall') : t('app:toolkit_install')} | ||
| </Button> | ||
| )} | ||
| <Box | ||
| className="author-info" | ||
| color={'myGray.500'} | ||
| mt={3} | ||
| >{`by ${item.author || systemTitle || 'FastGPT'}`}</Box> | ||
| {/*TODO: when statistics is ready*/} | ||
| {/*<Flex flexDirection={'row'} gap={1} className="download-count" color={'myGray.500'} mt={3}> | ||
| <MyIcon name="common/downloadLine" /> | ||
| {!item.downloadCount | ||
| ? 0 | ||
| : item.downloadCount < 1000 | ||
| ? `${item.downloadCount}` | ||
| : `${(item.downloadCount / 1000).toFixed(1)}k`} | ||
| </Flex>*/} | ||
|
|
||
| <Flex gap={2} alignItems={'center'} ml={'auto'}> | ||
| {mode === 'marketplace' ? ( | ||
| <Button | ||
| className="install-button" | ||
| size={'sm'} | ||
| variant={'primary'} | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| onInstall(); | ||
| }} | ||
| isLoading={isInstallingOrDeleting} | ||
| {...(!isInstallingOrDeleting ? { display: 'none' } : {})} | ||
| > | ||
| {t('common:Download')} | ||
| </Button> | ||
| ) : ( | ||
| <Button | ||
| className="install-button" | ||
| size={'sm'} | ||
| variant={item.installed ? 'primaryOutline' : 'primary'} | ||
| onClick={async (e) => { | ||
| e.stopPropagation(); | ||
| if (item.installed) { | ||
| // delete | ||
| if (onDelete) { | ||
| return onDelete(); | ||
| } | ||
| } else { | ||
| return onInstall(); | ||
| } | ||
| }} | ||
| isLoading={isInstallingOrDeleting} | ||
| {...(!isInstallingOrDeleting ? { display: 'none' } : {})} | ||
| disabled={isUpdating} | ||
|
||
| > | ||
| {item.installed ? t('app:toolkit_uninstall') : t('app:toolkit_install')} | ||
| </Button> | ||
| )} | ||
|
|
||
| {/* Update button for admin mode when update is available */} | ||
| {item.update && mode === 'admin' && ( | ||
| <Button | ||
| className="update-button" | ||
| size={'sm'} | ||
| variant={'primary'} | ||
| onClick={async (e) => { | ||
| e.stopPropagation(); | ||
| return onUpdate?.(); | ||
|
Comment on lines
+318
to
+325
|
||
| }} | ||
| isLoading={isUpdating} | ||
| display={'none'} | ||
| > | ||
| {t('app:custom_plugin_update')} | ||
| </Button> | ||
| )} | ||
| </Flex> | ||
| </Flex> | ||
| </MyBox> | ||
| ); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type inconsistency: The schema still includes
downloadUrl: stringin the type definition (line 14), but this property is being replaced withdownloadCount: numberaccording to the changes in the API response. The type definition should be updated to reflect this change: removedownloadUrland ensuredownloadCountis the only download-related property, or both should be present if both are needed.