From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001 From: wwf <yearningwang@iqtogether.com> Date: 星期三, 04 六月 2025 15:17:49 +0800 Subject: [PATCH] 初始化 --- app/components/tools/provider-list.tsx | 194 +++++++++++++++++++----------------------------- 1 files changed, 77 insertions(+), 117 deletions(-) diff --git a/app/components/tools/provider-list.tsx b/app/components/tools/provider-list.tsx index b1da6d5..73c7363 100644 --- a/app/components/tools/provider-list.tsx +++ b/app/components/tools/provider-list.tsx @@ -1,36 +1,33 @@ 'use client' -import { useMemo, useRef, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' +import { RiCloseLine } from '@remixicon/react' import type { Collection } from './types' -import Marketplace from './marketplace' import cn from '@/utils/classnames' import { useTabSearchParams } from '@/hooks/use-tab-searchparams' import TabSliderNew from '@/app/components/base/tab-slider-new' import LabelFilter from '@/app/components/tools/labels/filter' import Input from '@/app/components/base/input' -import ProviderDetail from '@/app/components/tools/provider/detail' -import Empty from '@/app/components/plugins/marketplace/empty' +import { DotsGrid } from '@/app/components/base/icons/src/vender/line/general' +import { Colors } from '@/app/components/base/icons/src/vender/line/others' +import { Route } from '@/app/components/base/icons/src/vender/line/mapsAndTravel' import CustomCreateCard from '@/app/components/tools/provider/custom-create-card' -import WorkflowToolEmpty from '@/app/components/tools/add-tool-modal/empty' -import Card from '@/app/components/plugins/card' -import CardMoreInfo from '@/app/components/plugins/card/card-more-info' -import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' -import { useSelector as useAppContextSelector } from '@/context/app-context' -import { useAllToolProviders } from '@/service/use-tools' -import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' +import ContributeCard from '@/app/components/tools/provider/contribute' +import ProviderCard from '@/app/components/tools/provider/card' +import ProviderDetail from '@/app/components/tools/provider/detail' +import Empty from '@/app/components/tools/add-tool-modal/empty' +import { fetchCollectionList } from '@/service/tools' const ProviderList = () => { const { t } = useTranslation() - const containerRef = useRef<HTMLDivElement>(null) - const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures) const [activeTab, setActiveTab] = useTabSearchParams({ defaultTab: 'builtin', }) const options = [ - { value: 'builtin', text: t('tools.type.builtIn') }, - { value: 'api', text: t('tools.type.custom') }, - { value: 'workflow', text: t('tools.type.workflow') }, + { value: 'builtin', text: t('tools.type.builtIn'), icon: <DotsGrid className='w-[14px] h-[14px] mr-1' /> }, + { value: 'api', text: t('tools.type.custom'), icon: <Colors className='w-[14px] h-[14px] mr-1' /> }, + { value: 'workflow', text: t('tools.type.workflow'), icon: <Route className='w-[14px] h-[14px] mr-1' /> }, ] const [tagFilterValue, setTagFilterValue] = useState<string[]>([]) const handleTagsChange = (value: string[]) => { @@ -40,7 +37,8 @@ const handleKeywordsChange = (value: string) => { setKeywords(value) } - const { data: collectionList = [], refetch } = useAllToolProviders() + + const [collectionList, setCollectionList] = useState<Collection[]>([]) const filteredCollectionList = useMemo(() => { return collectionList.filter((collection) => { if (collection.type !== activeTab) @@ -52,113 +50,75 @@ return true }) }, [activeTab, tagFilterValue, keywords, collectionList]) + const getProviderList = async () => { + const list = await fetchCollectionList() + setCollectionList([...list]) + } + useEffect(() => { + getProviderList() + }, []) - const [currentProviderId, setCurrentProviderId] = useState<string | undefined>() - const currentProvider = useMemo<Collection | undefined>(() => { - return filteredCollectionList.find(collection => collection.id === currentProviderId) - }, [currentProviderId, filteredCollectionList]) - const { data: pluginList } = useInstalledPluginList() - const invalidateInstalledPluginList = useInvalidateInstalledPluginList() - const currentPluginDetail = useMemo(() => { - const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentProvider?.plugin_id) - return detail - }, [currentProvider?.plugin_id, pluginList?.plugins]) + const [currentProvider, setCurrentProvider] = useState<Collection | undefined>() + useEffect(() => { + if (currentProvider && collectionList.length > 0) { + const newCurrentProvider = collectionList.find(collection => collection.id === currentProvider.id) + setCurrentProvider(newCurrentProvider) + } + }, [collectionList, currentProvider]) return ( - <> - <div className='relative flex h-0 shrink-0 grow overflow-hidden'> - <div - ref={containerRef} - className='relative flex grow flex-col overflow-y-auto bg-background-body' - > - <div className={cn( - 'sticky top-0 z-20 flex flex-wrap items-center justify-between gap-y-2 bg-background-body px-12 pb-2 pt-4 leading-[56px]', - currentProviderId && 'pr-6', - )}> - <TabSliderNew - value={activeTab} - onChange={(state) => { - setActiveTab(state) - if (state !== activeTab) - setCurrentProviderId(undefined) - }} - options={options} + <div className='relative flex overflow-hidden bg-gray-100 shrink-0 h-0 grow'> + <div className='relative flex flex-col overflow-y-auto bg-gray-100 grow'> + <div className={cn( + 'sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-20 flex-wrap gap-y-2', + currentProvider && 'pr-6', + )}> + <TabSliderNew + value={activeTab} + onChange={(state) => { + setActiveTab(state) + if (state !== activeTab) + setCurrentProvider(undefined) + }} + options={options} + /> + <div className='flex items-center gap-2'> + <LabelFilter value={tagFilterValue} onChange={handleTagsChange} /> + <Input + showLeftIcon + showClearIcon + wrapperClassName='w-[200px]' + value={keywords} + onChange={e => handleKeywordsChange(e.target.value)} + onClear={() => handleKeywordsChange('')} /> - <div className='flex items-center gap-2'> - <LabelFilter value={tagFilterValue} onChange={handleTagsChange} /> - <Input - showLeftIcon - showClearIcon - wrapperClassName='w-[200px]' - value={keywords} - onChange={e => handleKeywordsChange(e.target.value)} - onClear={() => handleKeywordsChange('')} - /> - </div> </div> - {(filteredCollectionList.length > 0 || activeTab !== 'builtin') && ( - <div className={cn( - 'relative grid shrink-0 grid-cols-1 content-start gap-4 px-12 pb-4 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4', - !filteredCollectionList.length && activeTab === 'workflow' && 'grow', - )}> - {activeTab === 'api' && <CustomCreateCard onRefreshData={refetch} />} - {filteredCollectionList.map(collection => ( - <div - key={collection.id} - onClick={() => setCurrentProviderId(collection.id)} - > - <Card - className={cn( - 'cursor-pointer border-[1.5px] border-transparent', - currentProviderId === collection.id && 'border-components-option-card-option-selected-border', - )} - hideCornerMark - payload={{ - ...collection, - brief: collection.description, - org: collection.plugin_id ? collection.plugin_id.split('/')[0] : '', - name: collection.plugin_id ? collection.plugin_id.split('/')[1] : collection.name, - } as any} - footer={ - <CardMoreInfo - tags={collection.labels} - /> - } - /> - </div> - ))} - {!filteredCollectionList.length && activeTab === 'workflow' && <div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'><WorkflowToolEmpty /></div>} - </div> - )} - {!filteredCollectionList.length && activeTab === 'builtin' && ( - <Empty lightCard text={t('tools.noTools')} className='h-[224px] px-12' /> - )} - { - enable_marketplace && activeTab === 'builtin' && ( - <Marketplace - onMarketplaceScroll={() => { - containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'smooth' }) - }} - searchPluginText={keywords} - filterPluginTags={tagFilterValue} - /> - ) - } + </div> + <div className={cn( + 'relative grid content-start grid-cols-1 gap-4 px-12 pt-2 pb-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0', + currentProvider && 'pr-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3', + )}> + {activeTab === 'builtin' && <ContributeCard />} + {activeTab === 'api' && <CustomCreateCard onRefreshData={getProviderList} />} + {filteredCollectionList.map(collection => ( + <ProviderCard + active={currentProvider?.id === collection.id} + onSelect={() => setCurrentProvider(collection)} + key={collection.id} + collection={collection} + /> + ))} + {!filteredCollectionList.length && <div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'><Empty /></div>} </div> </div> - {currentProvider && !currentProvider.plugin_id && ( - <ProviderDetail - collection={currentProvider} - onHide={() => setCurrentProviderId(undefined)} - onRefreshData={refetch} - /> - )} - <PluginDetailPanel - detail={currentPluginDetail} - onUpdate={() => invalidateInstalledPluginList()} - onHide={() => setCurrentProviderId(undefined)} - /> - </> + <div className={cn( + 'shrink-0 w-0 border-l-[0.5px] border-black/8 overflow-y-auto transition-all duration-200 ease-in-out', + currentProvider && 'w-[420px]', + )}> + {currentProvider && <ProviderDetail collection={currentProvider} onRefreshData={getProviderList} />} + </div> + <div className='absolute top-5 right-5 p-1 cursor-pointer' onClick={() => setCurrentProvider(undefined)}><RiCloseLine className='w-4 h-4' /></div> + </div> ) } ProviderList.displayName = 'ToolProviderList' -- Gitblit v1.8.0