Co-authored-by: crazywoola <427733928@qq.com>tags/0.5.7
| import { useAppContext } from '@/context/app-context' | import { useAppContext } from '@/context/app-context' | ||||
| import { NEED_REFRESH_APP_LIST_KEY } from '@/config' | import { NEED_REFRESH_APP_LIST_KEY } from '@/config' | ||||
| import { CheckModal } from '@/hooks/use-pay' | import { CheckModal } from '@/hooks/use-pay' | ||||
| import { useTabSearchParams } from '@/hooks/use-tab-searchparams' | |||||
| import TabSlider from '@/app/components/base/tab-slider' | import TabSlider from '@/app/components/base/tab-slider' | ||||
| import { SearchLg } from '@/app/components/base/icons/src/vender/line/general' | import { SearchLg } from '@/app/components/base/icons/src/vender/line/general' | ||||
| import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' | import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' | ||||
| const Apps = () => { | const Apps = () => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { isCurrentWorkspaceManager } = useAppContext() | const { isCurrentWorkspaceManager } = useAppContext() | ||||
| const [activeTab, setActiveTab] = useState('all') | |||||
| const [activeTab, setActiveTab] = useTabSearchParams({ | |||||
| defaultTab: 'all', | |||||
| }) | |||||
| const [keywords, setKeywords] = useState('') | const [keywords, setKeywords] = useState('') | ||||
| const [searchKeywords, setSearchKeywords] = useState('') | const [searchKeywords, setSearchKeywords] = useState('') | ||||
| 'use client' | 'use client' | ||||
| // Libraries | // Libraries | ||||
| import { useRef, useState } from 'react' | |||||
| import { useRef } from 'react' | |||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import useSWR from 'swr' | import useSWR from 'swr' | ||||
| // Services | // Services | ||||
| import { fetchDatasetApiBaseUrl } from '@/service/datasets' | import { fetchDatasetApiBaseUrl } from '@/service/datasets' | ||||
| // Hooks | |||||
| import { useTabSearchParams } from '@/hooks/use-tab-searchparams' | |||||
| const Container = () => { | const Container = () => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| { value: 'api', text: t('dataset.datasetsApi') }, | { value: 'api', text: t('dataset.datasetsApi') }, | ||||
| ] | ] | ||||
| const [activeTab, setActiveTab] = useState('dataset') | |||||
| const [activeTab, setActiveTab] = useTabSearchParams({ | |||||
| defaultTab: 'dataset', | |||||
| }) | |||||
| const containerRef = useRef<HTMLDivElement>(null) | const containerRef = useRef<HTMLDivElement>(null) | ||||
| const { data } = useSWR(activeTab === 'dataset' ? null : '/datasets/api-base-info', fetchDatasetApiBaseUrl) | const { data } = useSWR(activeTab === 'dataset' ? null : '/datasets/api-base-info', fetchDatasetApiBaseUrl) | ||||
| const { locale } = useContext(I18n) | const { locale } = useContext(I18n) | ||||
| const { CSVDownloader, Type } = useCSVDownloader() | const { CSVDownloader, Type } = useCSVDownloader() | ||||
| const [list, setList] = useState<AnnotationItemBasic[]>([]) | const [list, setList] = useState<AnnotationItemBasic[]>([]) | ||||
| const annotationUnavailable = list.length === 0 | |||||
| const listTransformer = (list: AnnotationItemBasic[]) => list.map( | const listTransformer = (list: AnnotationItemBasic[]) => list.map( | ||||
| (item: AnnotationItemBasic) => { | (item: AnnotationItemBasic) => { | ||||
| ...list.map(item => [item.question, item.answer]), | ...list.map(item => [item.question, item.answer]), | ||||
| ]} | ]} | ||||
| > | > | ||||
| <button className={s.actionItem}> | |||||
| <button disabled={annotationUnavailable} className={s.actionItem}> | |||||
| <span className={s.actionName}>CSV</span> | <span className={s.actionName}>CSV</span> | ||||
| </button> | </button> | ||||
| </CSVDownloader> | </CSVDownloader> | ||||
| <button className={cn(s.actionItem, '!border-0')} onClick={JSONLOutput}> | |||||
| <button disabled={annotationUnavailable} className={cn(s.actionItem, '!border-0')} onClick={JSONLOutput}> | |||||
| <span className={s.actionName}>JSONL</span> | <span className={s.actionName}>JSONL</span> | ||||
| </button> | </button> | ||||
| </Menu.Items> | </Menu.Items> |
| } | } | ||||
| .actionItem { | .actionItem { | ||||
| @apply h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-gray-100 rounded-lg cursor-pointer; | |||||
| @apply h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-gray-100 rounded-lg cursor-pointer disabled:opacity-50; | |||||
| width: calc(100% - 0.5rem); | width: calc(100% - 0.5rem); | ||||
| } | } | ||||
| left: 4px; | left: 4px; | ||||
| transform: translateX(-100%); | transform: translateX(-100%); | ||||
| box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03); | box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03); | ||||
| } | |||||
| } |
| import AppCard from '@/app/components/explore/app-card' | import AppCard from '@/app/components/explore/app-card' | ||||
| import { fetchAppDetail, fetchAppList } from '@/service/explore' | import { fetchAppDetail, fetchAppList } from '@/service/explore' | ||||
| import { createApp } from '@/service/apps' | import { createApp } from '@/service/apps' | ||||
| import { useTabSearchParams } from '@/hooks/use-tab-searchparams' | |||||
| import CreateAppModal from '@/app/components/explore/create-app-modal' | import CreateAppModal from '@/app/components/explore/create-app-modal' | ||||
| import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' | import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' | ||||
| import Loading from '@/app/components/base/loading' | import Loading from '@/app/components/base/loading' | ||||
| const { isCurrentWorkspaceManager } = useAppContext() | const { isCurrentWorkspaceManager } = useAppContext() | ||||
| const router = useRouter() | const router = useRouter() | ||||
| const { hasEditPermission } = useContext(ExploreContext) | const { hasEditPermission } = useContext(ExploreContext) | ||||
| const [currCategory, setCurrCategory] = React.useState<AppCategory | ''>('') | |||||
| const [currCategory, setCurrCategory] = useTabSearchParams({ | |||||
| defaultTab: '', | |||||
| }) | |||||
| const [allList, setAllList] = React.useState<App[]>([]) | const [allList, setAllList] = React.useState<App[]>([]) | ||||
| const [isLoaded, setIsLoaded] = React.useState(false) | const [isLoaded, setIsLoaded] = React.useState(false) | ||||
| import NoCustomTool from './info/no-custom-tool' | import NoCustomTool from './info/no-custom-tool' | ||||
| import NoSearchRes from './info/no-search-res' | import NoSearchRes from './info/no-search-res' | ||||
| import NoCustomToolPlaceholder from './no-custom-tool-placeholder' | import NoCustomToolPlaceholder from './no-custom-tool-placeholder' | ||||
| import { useTabSearchParams } from '@/hooks/use-tab-searchparams' | |||||
| import TabSlider from '@/app/components/base/tab-slider' | import TabSlider from '@/app/components/base/tab-slider' | ||||
| import { createCustomCollection, fetchCollectionList as doFetchCollectionList, fetchBuiltInToolList, fetchCustomToolList } from '@/service/tools' | import { createCustomCollection, fetchCollectionList as doFetchCollectionList, fetchBuiltInToolList, fetchCustomToolList } from '@/service/tools' | ||||
| import type { AgentTool } from '@/types/app' | import type { AgentTool } from '@/types/app' | ||||
| })() | })() | ||||
| const [query, setQuery] = useState('') | const [query, setQuery] = useState('') | ||||
| const [collectionType, setCollectionType] = useState<CollectionType>(collectionTypeOptions[0].value) | |||||
| const [collectionType, setCollectionType] = useTabSearchParams({ | |||||
| defaultTab: collectionTypeOptions[0].value, | |||||
| }) | |||||
| const showCollectionList = (() => { | const showCollectionList = (() => { | ||||
| let typeFilteredList: Collection[] = [] | let typeFilteredList: Collection[] = [] |
| import { usePathname, useRouter, useSearchParams } from 'next/navigation' | |||||
| type UseTabSearchParamsOptions = { | |||||
| defaultTab: string | |||||
| routingBehavior?: 'push' | 'replace' | |||||
| searchParamName?: string | |||||
| } | |||||
| /** | |||||
| * Custom hook to manage tab state via URL search parameters in a Next.js application. | |||||
| * This hook allows for syncing the active tab with the browser's URL, enabling bookmarking and sharing of URLs with a specific tab activated. | |||||
| * | |||||
| * @param {UseTabSearchParamsOptions} options Configuration options for the hook: | |||||
| * - `defaultTab`: The tab to default to when no tab is specified in the URL. | |||||
| * - `routingBehavior`: Optional. Determines how changes to the active tab update the browser's history ('push' or 'replace'). Default is 'push'. | |||||
| * - `searchParamName`: Optional. The name of the search parameter that holds the tab state in the URL. Default is 'category'. | |||||
| * @returns A tuple where the first element is the active tab and the second element is a function to set the active tab. | |||||
| */ | |||||
| export const useTabSearchParams = ({ | |||||
| defaultTab, | |||||
| routingBehavior = 'push', | |||||
| searchParamName = 'category', | |||||
| }: UseTabSearchParamsOptions) => { | |||||
| const router = useRouter() | |||||
| const pathName = usePathname() | |||||
| const searchParams = useSearchParams() | |||||
| const activeTab = searchParams.get(searchParamName) || defaultTab | |||||
| const setActiveTab = (newActiveTab: string) => { | |||||
| router[routingBehavior](`${pathName}?${searchParamName}=${newActiveTab}`) | |||||
| } | |||||
| return [activeTab, setActiveTab] as const | |||||
| } |