| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | 'use client' | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | import { useMemo, useRef, useState } from 'react' | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | import { useTranslation } from 'react-i18next' | 
					
						
							|  |  |  | import type { Collection } from './types' | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | import Marketplace from './marketplace' | 
					
						
							| 
									
										
										
										
											2024-07-09 15:05:40 +08:00
										 |  |  | import cn from '@/utils/classnames' | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | import { useTabSearchParams } from '@/hooks/use-tab-searchparams' | 
					
						
							|  |  |  | import TabSliderNew from '@/app/components/base/tab-slider-new' | 
					
						
							|  |  |  | import LabelFilter from '@/app/components/tools/labels/filter' | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  | import Input from '@/app/components/base/input' | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | import ProviderDetail from '@/app/components/tools/provider/detail' | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | import Empty from '@/app/components/plugins/marketplace/empty' | 
					
						
							|  |  |  | 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 { useAllToolProviders } from '@/service/use-tools' | 
					
						
							|  |  |  | import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' | 
					
						
							| 
									
										
										
										
											2025-05-20 12:07:50 +08:00
										 |  |  | import { useGlobalPublicStore } from '@/context/global-public-context' | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | const ProviderList = () => { | 
					
						
							|  |  |  |   const { t } = useTranslation() | 
					
						
							| 
									
										
										
										
											2025-05-20 12:07:50 +08:00
										 |  |  |   const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |   const containerRef = useRef<HTMLDivElement>(null) | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const [activeTab, setActiveTab] = useTabSearchParams({ | 
					
						
							|  |  |  |     defaultTab: 'builtin', | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  |   const options = [ | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |     { value: 'builtin', text: t('tools.type.builtIn') }, | 
					
						
							|  |  |  |     { value: 'api', text: t('tools.type.custom') }, | 
					
						
							|  |  |  |     { value: 'workflow', text: t('tools.type.workflow') }, | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   ] | 
					
						
							|  |  |  |   const [tagFilterValue, setTagFilterValue] = useState<string[]>([]) | 
					
						
							|  |  |  |   const handleTagsChange = (value: string[]) => { | 
					
						
							|  |  |  |     setTagFilterValue(value) | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const [keywords, setKeywords] = useState<string>('') | 
					
						
							|  |  |  |   const handleKeywordsChange = (value: string) => { | 
					
						
							|  |  |  |     setKeywords(value) | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |   const { data: collectionList = [], refetch } = useAllToolProviders() | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   const filteredCollectionList = useMemo(() => { | 
					
						
							|  |  |  |     return collectionList.filter((collection) => { | 
					
						
							|  |  |  |       if (collection.type !== activeTab) | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |       if (tagFilterValue.length > 0 && (!collection.labels || collection.labels.every(label => !tagFilterValue.includes(label)))) | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |       if (keywords) | 
					
						
							| 
									
										
										
										
											2025-01-22 19:27:02 +08:00
										 |  |  |         return Object.values(collection.label).some(value => value.toLowerCase().includes(keywords.toLowerCase())) | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |       return true | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   }, [activeTab, tagFilterValue, keywords, collectionList]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |   const [currentProviderId, setCurrentProviderId] = useState<string | undefined>() | 
					
						
							|  |  |  |   const currentProvider = useMemo<Collection | undefined>(() => { | 
					
						
							|  |  |  |     return filteredCollectionList.find(collection => collection.id === currentProviderId) | 
					
						
							|  |  |  |   }, [currentProviderId, filteredCollectionList]) | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |   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]) | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |     <> | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |       <div className='relative flex h-0 shrink-0 grow overflow-hidden'> | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |         <div | 
					
						
							|  |  |  |           ref={containerRef} | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |           className='relative flex grow flex-col overflow-y-auto bg-background-body' | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |         > | 
					
						
							|  |  |  |           <div className={cn( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             '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]', | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |             currentProviderId && 'pr-6', | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |           )}> | 
					
						
							|  |  |  |             <TabSliderNew | 
					
						
							|  |  |  |               value={activeTab} | 
					
						
							|  |  |  |               onChange={(state) => { | 
					
						
							|  |  |  |                 setActiveTab(state) | 
					
						
							|  |  |  |                 if (state !== activeTab) | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |                   setCurrentProviderId(undefined) | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |               }} | 
					
						
							|  |  |  |               options={options} | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |             /> | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |             <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> | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |           {(filteredCollectionList.length > 0 || activeTab !== 'builtin') && ( | 
					
						
							|  |  |  |             <div className={cn( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |               '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', | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |               !filteredCollectionList.length && activeTab === 'workflow' && 'grow', | 
					
						
							|  |  |  |             )}> | 
					
						
							|  |  |  |               {activeTab === 'api' && <CustomCreateCard onRefreshData={refetch} />} | 
					
						
							|  |  |  |               {filteredCollectionList.map(collection => ( | 
					
						
							|  |  |  |                 <div | 
					
						
							|  |  |  |                   key={collection.id} | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |                   onClick={() => setCurrentProviderId(collection.id)} | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |                 > | 
					
						
							|  |  |  |                   <Card | 
					
						
							|  |  |  |                     className={cn( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                       'cursor-pointer border-[1.5px] border-transparent', | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |                       currentProviderId === collection.id && 'border-components-option-card-option-selected-border', | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |                     )} | 
					
						
							|  |  |  |                     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> | 
					
						
							|  |  |  |               ))} | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |               {!filteredCollectionList.length && activeTab === 'workflow' && <div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'><WorkflowToolEmpty /></div>} | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |             </div> | 
					
						
							|  |  |  |           )} | 
					
						
							|  |  |  |           {!filteredCollectionList.length && activeTab === 'builtin' && ( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             <Empty lightCard text={t('tools.noTools')} className='h-[224px] px-12' /> | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |           )} | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             enable_marketplace && activeTab === 'builtin' && ( | 
					
						
							|  |  |  |               <Marketplace | 
					
						
							|  |  |  |                 onMarketplaceScroll={() => { | 
					
						
							|  |  |  |                   containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'smooth' }) | 
					
						
							|  |  |  |                 }} | 
					
						
							|  |  |  |                 searchPluginText={keywords} | 
					
						
							|  |  |  |                 filterPluginTags={tagFilterValue} | 
					
						
							|  |  |  |               /> | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2025-05-20 12:07:50 +08:00
										 |  |  |         </div > | 
					
						
							|  |  |  |       </div > | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |       {currentProvider && !currentProvider.plugin_id && ( | 
					
						
							|  |  |  |         <ProviderDetail | 
					
						
							|  |  |  |           collection={currentProvider} | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |           onHide={() => setCurrentProviderId(undefined)} | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |           onRefreshData={refetch} | 
					
						
							|  |  |  |         /> | 
					
						
							|  |  |  |       )} | 
					
						
							|  |  |  |       <PluginDetailPanel | 
					
						
							|  |  |  |         detail={currentPluginDetail} | 
					
						
							|  |  |  |         onUpdate={() => invalidateInstalledPluginList()} | 
					
						
							| 
									
										
										
										
											2025-05-13 13:47:15 +08:00
										 |  |  |         onHide={() => setCurrentProviderId(undefined)} | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |       /> | 
					
						
							|  |  |  |     </> | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   ) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ProviderList.displayName = 'ToolProviderList' | 
					
						
							|  |  |  | export default ProviderList |