✨(frontend) handle sub-docs
handle filtering on sub-docs
This commit is contained in:
@@ -9,5 +9,5 @@ export * from './useDocsFavorite';
|
||||
export * from './useDuplicateDoc';
|
||||
export * from './useMoveDoc';
|
||||
export * from './useRestoreDoc';
|
||||
export * from './useSubDocs';
|
||||
export * from './useUpdateDoc';
|
||||
export * from './useSearchDocs';
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { APIError, APIList, errorCauses, fetchAPI } from '@/api';
|
||||
import { KEY_LIST_DOC } from '@/docs/doc-management';
|
||||
|
||||
import { Doc } from '../types';
|
||||
|
||||
export type DocsParams = {
|
||||
q: string;
|
||||
};
|
||||
|
||||
export const constructParams = (params: DocsParams): URLSearchParams => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
if (params.q.length > 0) {
|
||||
searchParams.set('q', params.q);
|
||||
} else {
|
||||
searchParams.set('q', '*');
|
||||
}
|
||||
|
||||
return searchParams;
|
||||
};
|
||||
|
||||
export type DocsResponse = APIList<Doc>;
|
||||
export const getDocs = async (params: DocsParams): Promise<DocsResponse> => {
|
||||
const searchParams = constructParams(params);
|
||||
const response = await fetchAPI(
|
||||
`documents/search/?${searchParams.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError('Failed to get the docs', await errorCauses(response));
|
||||
}
|
||||
|
||||
return response.json() as Promise<DocsResponse>;
|
||||
};
|
||||
|
||||
export const useSearchDocs = (params: DocsParams) => {
|
||||
return useQuery<DocsResponse, APIError, DocsResponse>({
|
||||
queryKey: [KEY_LIST_DOC, 'search', params],
|
||||
queryFn: () => getDocs(params),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import {
|
||||
APIError,
|
||||
APIList,
|
||||
errorCauses,
|
||||
fetchAPI,
|
||||
useAPIInfiniteQuery,
|
||||
} from '@/api';
|
||||
import { Doc } from '@/docs/doc-management';
|
||||
import { DocSearchTarget } from '@/docs/doc-search';
|
||||
|
||||
export type SearchDocsParams = {
|
||||
page: number;
|
||||
q: string;
|
||||
target?: DocSearchTarget;
|
||||
parentPath?: string;
|
||||
};
|
||||
|
||||
const constructParams = ({
|
||||
q,
|
||||
page,
|
||||
target,
|
||||
parentPath,
|
||||
}: SearchDocsParams): URLSearchParams => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
searchParams.set('q', q);
|
||||
|
||||
if (target === DocSearchTarget.CURRENT && parentPath) {
|
||||
searchParams.set('path', parentPath);
|
||||
}
|
||||
if (page) {
|
||||
searchParams.set('page', page.toString());
|
||||
}
|
||||
|
||||
return searchParams;
|
||||
};
|
||||
|
||||
const searchDocs = async ({
|
||||
q,
|
||||
page,
|
||||
target,
|
||||
parentPath,
|
||||
}: SearchDocsParams): Promise<APIList<Doc>> => {
|
||||
const searchParams = constructParams({ q, page, target, parentPath });
|
||||
const response = await fetchAPI(
|
||||
`documents/search/?${searchParams.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError('Failed to get the docs', await errorCauses(response));
|
||||
}
|
||||
|
||||
return response.json() as Promise<APIList<Doc>>;
|
||||
};
|
||||
|
||||
export const KEY_LIST_SEARCH_DOC = 'search-docs';
|
||||
|
||||
export const useSearchDocs = (
|
||||
{ q, page, target, parentPath }: SearchDocsParams,
|
||||
queryConfig?: { enabled?: boolean },
|
||||
) => {
|
||||
return useQuery<APIList<Doc>, APIError, APIList<Doc>>({
|
||||
queryKey: [KEY_LIST_SEARCH_DOC, 'search', { q, page, target, parentPath }],
|
||||
queryFn: () => searchDocs({ q, page, target, parentPath }),
|
||||
...queryConfig,
|
||||
});
|
||||
};
|
||||
|
||||
export const useInfiniteSearchDocs = (
|
||||
params: SearchDocsParams,
|
||||
queryConfig?: { enabled?: boolean },
|
||||
) => {
|
||||
return useAPIInfiniteQuery(
|
||||
KEY_LIST_SEARCH_DOC,
|
||||
searchDocs,
|
||||
params,
|
||||
queryConfig,
|
||||
);
|
||||
};
|
||||
+11
-4
@@ -1,14 +1,13 @@
|
||||
import { t } from 'i18next';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
|
||||
import { Box } from '@/components/';
|
||||
import { QuickSearchData, QuickSearchGroup } from '@/components/quick-search';
|
||||
import { useSearchDocs } from '@/docs/doc-management/api/searchDocs';
|
||||
import { useInfiniteSearchDocs } from '@/docs/doc-management/api/useSearchDocs';
|
||||
|
||||
import { Doc } from '../../doc-management';
|
||||
|
||||
import { DocSearchFiltersValues } from './DocSearchFilters';
|
||||
import { DocSearchItem } from './DocSearchItem';
|
||||
|
||||
type DocSearchContentProps = {
|
||||
@@ -30,7 +29,15 @@ export const DocSearchContent = ({
|
||||
renderSearchElement,
|
||||
isSearchNotMandatory,
|
||||
}: DocSearchContentProps) => {
|
||||
const { data, isFetching, isRefetching, isLoading } = useSearchDocs({
|
||||
const {
|
||||
data,
|
||||
isFetching,
|
||||
isRefetching,
|
||||
isLoading,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
} = useInfiniteSearchDocs({
|
||||
page: 1,
|
||||
q: search,
|
||||
});
|
||||
|
||||
|
||||
@@ -133,7 +133,6 @@ const DocSearchModalGlobal = ({
|
||||
{isDocPage && target === DocSearchTarget.CURRENT && (
|
||||
<DocSearchSubPageContent
|
||||
search={search}
|
||||
filters={filters}
|
||||
onSelect={handleSelect}
|
||||
onLoadingChange={setLoading}
|
||||
renderElement={(doc) => <DocSearchItem doc={doc} />}
|
||||
|
||||
+9
-20
@@ -4,13 +4,12 @@ import React, { useEffect, useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
|
||||
import { QuickSearchData, QuickSearchGroup } from '@/components/quick-search';
|
||||
import { Doc, useInfiniteSubDocs } from '@/docs/doc-management';
|
||||
|
||||
import { DocSearchFiltersValues } from './DocSearchFilters';
|
||||
import { Doc } from '@/docs/doc-management';
|
||||
import { useInfiniteSearchDocs } from '@/docs/doc-management/api/useSearchDocs';
|
||||
import { DocSearchTarget } from '@/docs/doc-search';
|
||||
|
||||
type DocSearchSubPageContentProps = {
|
||||
search: string;
|
||||
filters: DocSearchFiltersValues;
|
||||
onSelect: (doc: Doc) => void;
|
||||
onLoadingChange?: (loading: boolean) => void;
|
||||
renderElement: (doc: Doc) => React.ReactNode;
|
||||
@@ -18,7 +17,6 @@ type DocSearchSubPageContentProps = {
|
||||
|
||||
export const DocSearchSubPageContent = ({
|
||||
search,
|
||||
filters,
|
||||
onSelect,
|
||||
onLoadingChange,
|
||||
renderElement,
|
||||
@@ -32,17 +30,18 @@ export const DocSearchSubPageContent = ({
|
||||
isLoading,
|
||||
fetchNextPage: subDocsFetchNextPage,
|
||||
hasNextPage: subDocsHasNextPage,
|
||||
} = useInfiniteSubDocs(
|
||||
} = useInfiniteSearchDocs(
|
||||
{
|
||||
q: search,
|
||||
page: 1,
|
||||
title: search,
|
||||
...filters,
|
||||
parent_id: treeContext?.root?.id ?? '',
|
||||
target: DocSearchTarget.CURRENT,
|
||||
parentPath: treeContext?.root?.path,
|
||||
},
|
||||
{
|
||||
enabled: !!treeContext?.root?.id,
|
||||
enabled: !!treeContext?.root?.path,
|
||||
},
|
||||
);
|
||||
|
||||
const [docsData, setDocsData] = useState<QuickSearchData<Doc>>({
|
||||
groupName: '',
|
||||
elements: [],
|
||||
@@ -58,16 +57,6 @@ export const DocSearchSubPageContent = ({
|
||||
|
||||
const subDocs = subDocsData?.pages.flatMap((page) => page.results) || [];
|
||||
|
||||
if (treeContext?.root) {
|
||||
const isRootTitleIncludeSearch = treeContext.root?.title
|
||||
?.toLowerCase()
|
||||
.includes(search.toLowerCase());
|
||||
|
||||
if (isRootTitleIncludeSearch) {
|
||||
subDocs.unshift(treeContext.root);
|
||||
}
|
||||
}
|
||||
|
||||
setDocsData({
|
||||
groupName: subDocs.length > 0 ? t('Select a doc') : '',
|
||||
elements: search ? subDocs : [],
|
||||
|
||||
Reference in New Issue
Block a user