diff --git a/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts b/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts new file mode 100644 index 000000000..4531b7e65 --- /dev/null +++ b/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts @@ -0,0 +1,83 @@ +import { type ReadonlyURLSearchParams, usePathname, useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useMemo } from 'react' + +export type DocumentListQuery = { + page: number + limit: number + keyword: string +} + +const DEFAULT_QUERY: DocumentListQuery = { + page: 1, + limit: 10, + keyword: '', +} + +// Parse the query parameters from the URL search string. +function parseParams(params: ReadonlyURLSearchParams): DocumentListQuery { + const page = Number.parseInt(params.get('page') || '1', 10) + const limit = Number.parseInt(params.get('limit') || '10', 10) + const keyword = params.get('keyword') || '' + + return { + page: page > 0 ? page : 1, + limit: (limit > 0 && limit <= 100) ? limit : 10, + keyword: keyword ? decodeURIComponent(keyword) : '', + } +} + +// Update the URL search string with the given query parameters. +function updateSearchParams(query: DocumentListQuery, searchParams: URLSearchParams) { + const { page, limit, keyword } = query || {} + + const hasNonDefaultParams = (page && page > 1) || (limit && limit !== 10) || (keyword && keyword.trim()) + + if (hasNonDefaultParams) { + searchParams.set('page', (page || 1).toString()) + searchParams.set('limit', (limit || 10).toString()) + } + else { + searchParams.delete('page') + searchParams.delete('limit') + } + + if (keyword && keyword.trim()) + searchParams.set('keyword', encodeURIComponent(keyword)) + else + searchParams.delete('keyword') +} + +function useDocumentListQueryState() { + const searchParams = useSearchParams() + const query = useMemo(() => parseParams(searchParams), [searchParams]) + + const router = useRouter() + const pathname = usePathname() + + // Helper function to update specific query parameters + const updateQuery = useCallback((updates: Partial) => { + const newQuery = { ...query, ...updates } + const params = new URLSearchParams() + updateSearchParams(newQuery, params) + const search = params.toString() + const queryString = search ? `?${search}` : '' + router.push(`${pathname}${queryString}`, { scroll: false }) + }, [query, router, pathname]) + + // Helper function to reset query to defaults + const resetQuery = useCallback(() => { + const params = new URLSearchParams() + updateSearchParams(DEFAULT_QUERY, params) + const search = params.toString() + const queryString = search ? `?${search}` : '' + router.push(`${pathname}${queryString}`, { scroll: false }) + }, [router, pathname]) + + return useMemo(() => ({ + query, + updateQuery, + resetQuery, + }), [query, updateQuery, resetQuery]) +} + +export default useDocumentListQueryState diff --git a/web/app/components/datasets/documents/index.tsx b/web/app/components/datasets/documents/index.tsx index 2840e5fa4..676581a50 100644 --- a/web/app/components/datasets/documents/index.tsx +++ b/web/app/components/datasets/documents/index.tsx @@ -26,6 +26,7 @@ import cn from '@/utils/classnames' import { useDocumentList, useInvalidDocumentDetailKey, useInvalidDocumentList } from '@/service/knowledge/use-document' import { useInvalid } from '@/service/use-base' import { useChildSegmentListKey, useSegmentListKey } from '@/service/knowledge/use-segment' +import useDocumentListQueryState from './hooks/use-document-list-query-state' import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata' import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer' import StatusWithAction from '../common/document-status-with-action/status-with-action' @@ -82,7 +83,6 @@ type IDocumentsProps = { } export const fetcher = (url: string) => get(url, {}, {}) -const DEFAULT_LIMIT = 10 const Documents: FC = ({ datasetId }) => { const { t } = useTranslation() @@ -91,8 +91,12 @@ const Documents: FC = ({ datasetId }) => { const isFreePlan = plan.type === 'sandbox' const [inputValue, setInputValue] = useState('') // the input value const [searchValue, setSearchValue] = useState('') - const [currPage, setCurrPage] = React.useState(0) - const [limit, setLimit] = useState(DEFAULT_LIMIT) + + // Use the new hook for URL state management + const { query, updateQuery } = useDocumentListQueryState() + const [currPage, setCurrPage] = React.useState(query.page - 1) // Convert to 0-based index + const [limit, setLimit] = useState(query.limit) + const router = useRouter() const { dataset } = useDatasetDetailContext() const [notionPageSelectorModalVisible, setNotionPageSelectorModalVisible] = useState(false) @@ -103,6 +107,45 @@ const Documents: FC = ({ datasetId }) => { const embeddingAvailable = !!dataset?.embedding_available const debouncedSearchValue = useDebounce(searchValue, { wait: 500 }) + // Initialize search value from URL on mount + useEffect(() => { + if (query.keyword) { + setInputValue(query.keyword) + setSearchValue(query.keyword) + } + }, []) // Only run on mount + + // Sync local state with URL query changes + useEffect(() => { + setCurrPage(query.page - 1) + setLimit(query.limit) + if (query.keyword !== searchValue) { + setInputValue(query.keyword) + setSearchValue(query.keyword) + } + }, [query]) + + // Update URL when pagination changes + const handlePageChange = (newPage: number) => { + setCurrPage(newPage) + updateQuery({ page: newPage + 1 }) // Convert to 1-based index + } + + // Update URL when limit changes + const handleLimitChange = (newLimit: number) => { + setLimit(newLimit) + setCurrPage(0) // Reset to first page when limit changes + updateQuery({ limit: newLimit, page: 1 }) + } + + // Update URL when search changes + useEffect(() => { + if (debouncedSearchValue !== query.keyword) { + setCurrPage(0) // Reset to first page when search changes + updateQuery({ keyword: debouncedSearchValue, page: 1 }) + } + }, [debouncedSearchValue, query.keyword, updateQuery]) + const { data: documentsRes, isFetching: isListLoading } = useDocumentList({ datasetId, query: { @@ -327,9 +370,9 @@ const Documents: FC = ({ datasetId }) => { pagination={{ total, limit, - onLimitChange: setLimit, + onLimitChange: handleLimitChange, current: currPage, - onChange: setCurrPage, + onChange: handlePageChange, }} onManageMetadata={showEditMetadataModal} /> diff --git a/web/app/components/datasets/documents/list.tsx b/web/app/components/datasets/documents/list.tsx index 4d566d923..c54b6a1a3 100644 --- a/web/app/components/datasets/documents/list.tsx +++ b/web/app/components/datasets/documents/list.tsx @@ -598,7 +598,6 @@ const DocumentList: FC = ({ ) }} /> - {/* {doc.position} */} {index + 1}