mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-15 21:16:01 +08:00
### What problem does this PR solve? Feat: Filter document by running status and file type. #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
dadd8d9f94
commit
bdebd1b2e3
@ -281,7 +281,10 @@ export function ChunkMethodDialog({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<DatasetConfigurationContainer show={showOne || showMaxTokenNumber}>
|
<DatasetConfigurationContainer
|
||||||
|
show={showOne || showMaxTokenNumber}
|
||||||
|
className="space-y-3"
|
||||||
|
>
|
||||||
{showOne && <LayoutRecognizeFormField></LayoutRecognizeFormField>}
|
{showOne && <LayoutRecognizeFormField></LayoutRecognizeFormField>}
|
||||||
{showMaxTokenNumber && (
|
{showMaxTokenNumber && (
|
||||||
<>
|
<>
|
||||||
@ -298,6 +301,7 @@ export function ChunkMethodDialog({
|
|||||||
</DatasetConfigurationContainer>
|
</DatasetConfigurationContainer>
|
||||||
<DatasetConfigurationContainer
|
<DatasetConfigurationContainer
|
||||||
show={showAutoKeywords(selectedTag) || showExcelToHtml}
|
show={showAutoKeywords(selectedTag) || showExcelToHtml}
|
||||||
|
className="space-y-3"
|
||||||
>
|
>
|
||||||
{showAutoKeywords(selectedTag) && (
|
{showAutoKeywords(selectedTag) && (
|
||||||
<>
|
<>
|
||||||
|
163
web/src/components/list-filter-bar/filter-popover.tsx
Normal file
163
web/src/components/list-filter-bar/filter-popover.tsx
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/components/ui/popover';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { PropsWithChildren, useCallback, useEffect } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { ZodArray, ZodString, z } from 'zod';
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/components/ui/form';
|
||||||
|
import { FilterChange, FilterCollection, FilterValue } from './interface';
|
||||||
|
|
||||||
|
export type CheckboxFormMultipleProps = {
|
||||||
|
filters?: FilterCollection[];
|
||||||
|
value?: FilterValue;
|
||||||
|
onChange?: FilterChange;
|
||||||
|
};
|
||||||
|
|
||||||
|
function CheckboxFormMultiple({
|
||||||
|
filters = [],
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: CheckboxFormMultipleProps) {
|
||||||
|
const fieldsDict = filters?.reduce<Record<string, Array<any>>>((pre, cur) => {
|
||||||
|
pre[cur.field] = [];
|
||||||
|
return pre;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const FormSchema = z.object(
|
||||||
|
filters.reduce<Record<string, ZodArray<ZodString, 'many'>>>((pre, cur) => {
|
||||||
|
pre[cur.field] = z.array(z.string());
|
||||||
|
|
||||||
|
// .refine((value) => value.some((item) => item), {
|
||||||
|
// message: 'You have to select at least one item.',
|
||||||
|
// });
|
||||||
|
return pre;
|
||||||
|
}, {}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: fieldsDict,
|
||||||
|
});
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
console.log('🚀 ~ onSubmit ~ data:', data);
|
||||||
|
// setOwnerIds(data.items);
|
||||||
|
onChange?.(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = useCallback(() => {
|
||||||
|
onChange?.(fieldsDict);
|
||||||
|
}, [fieldsDict, onChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.reset(value);
|
||||||
|
}, [form, value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-8"
|
||||||
|
onReset={() => form.reset()}
|
||||||
|
>
|
||||||
|
{filters.map((x) => (
|
||||||
|
<FormField
|
||||||
|
key={x.field}
|
||||||
|
control={form.control}
|
||||||
|
name={x.field}
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FormLabel className="text-base">{x.label}</FormLabel>
|
||||||
|
</div>
|
||||||
|
{x.list.map((item) => (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name={x.field}
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<FormItem
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-row space-x-3 space-y-0 items-center"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id)}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([...field.value, item.id])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== item.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="text-lg">
|
||||||
|
{item.label}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<span className=" text-sm">{item.count}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={'outline'}
|
||||||
|
size={'sm'}
|
||||||
|
onClick={onReset}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" size={'sm'}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FilterPopover({
|
||||||
|
children,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
filters,
|
||||||
|
}: PropsWithChildren & CheckboxFormMultipleProps) {
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<CheckboxFormMultiple
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
filters={filters}
|
||||||
|
></CheckboxFormMultiple>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
@ -1,19 +1,18 @@
|
|||||||
import { ChevronDown } from 'lucide-react';
|
import { ChevronDown } from 'lucide-react';
|
||||||
import React, {
|
import React, {
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
FunctionComponent,
|
|
||||||
PropsWithChildren,
|
PropsWithChildren,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
|
useMemo,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Button, ButtonProps } from './ui/button';
|
import { Button, ButtonProps } from '../ui/button';
|
||||||
import { SearchInput } from './ui/input';
|
import { SearchInput } from '../ui/input';
|
||||||
|
import { CheckboxFormMultipleProps, FilterPopover } from './filter-popover';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
FilterPopover?: FunctionComponent<any>;
|
|
||||||
searchString?: string;
|
searchString?: string;
|
||||||
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
|
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
|
||||||
count?: number;
|
|
||||||
showFilter?: boolean;
|
showFilter?: boolean;
|
||||||
leftPanel?: ReactNode;
|
leftPanel?: ReactNode;
|
||||||
}
|
}
|
||||||
@ -32,25 +31,31 @@ const FilterButton = React.forwardRef<
|
|||||||
export default function ListFilterBar({
|
export default function ListFilterBar({
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
FilterPopover,
|
|
||||||
searchString,
|
searchString,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
count,
|
|
||||||
showFilter = true,
|
showFilter = true,
|
||||||
leftPanel,
|
leftPanel,
|
||||||
}: PropsWithChildren<IProps>) {
|
value,
|
||||||
|
onChange,
|
||||||
|
filters,
|
||||||
|
}: PropsWithChildren<IProps & CheckboxFormMultipleProps>) {
|
||||||
|
const filterCount = useMemo(() => {
|
||||||
|
return typeof value === 'object' && value !== null
|
||||||
|
? Object.values(value).reduce((pre, cur) => {
|
||||||
|
return pre + cur.length;
|
||||||
|
}, 0)
|
||||||
|
: 0;
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between mb-6 items-center">
|
<div className="flex justify-between mb-6 items-center">
|
||||||
<span className="text-3xl font-bold ">{leftPanel || title}</span>
|
<span className="text-3xl font-bold ">{leftPanel || title}</span>
|
||||||
<div className="flex gap-4 items-center">
|
<div className="flex gap-4 items-center">
|
||||||
{showFilter &&
|
{showFilter && (
|
||||||
(FilterPopover ? (
|
<FilterPopover value={value} onChange={onChange} filters={filters}>
|
||||||
<FilterPopover>
|
<FilterButton count={filterCount}></FilterButton>
|
||||||
<FilterButton count={count}></FilterButton>
|
</FilterPopover>
|
||||||
</FilterPopover>
|
)}
|
||||||
) : (
|
|
||||||
<FilterButton></FilterButton>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<SearchInput
|
<SearchInput
|
||||||
value={searchString}
|
value={searchString}
|
15
web/src/components/list-filter-bar/interface.ts
Normal file
15
web/src/components/list-filter-bar/interface.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export type FilterType = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FilterCollection = {
|
||||||
|
field: string;
|
||||||
|
label: string;
|
||||||
|
list: FilterType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FilterValue = Record<string, Array<string>>;
|
||||||
|
|
||||||
|
export type FilterChange = (value: FilterValue) => void;
|
@ -0,0 +1,12 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { FilterChange, FilterValue } from './interface';
|
||||||
|
|
||||||
|
export function useHandleFilterSubmit() {
|
||||||
|
const [filterValue, setFilterValue] = useState<FilterValue>({});
|
||||||
|
|
||||||
|
const handleFilterSubmit: FilterChange = useCallback((value) => {
|
||||||
|
setFilterValue(value);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { filterValue, setFilterValue, handleFilterSubmit };
|
||||||
|
}
|
@ -76,7 +76,7 @@ const RaptorFormFields = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{useRaptor && (
|
{useRaptor && (
|
||||||
<>
|
<div className="space-y-3">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={'parser_config.raptor.prompt'}
|
name={'parser_config.raptor.prompt'}
|
||||||
@ -137,7 +137,7 @@ const RaptorFormFields = () => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from '@/interfaces/request/document';
|
} from '@/interfaces/request/document';
|
||||||
import i18n from '@/locales/config';
|
import i18n from '@/locales/config';
|
||||||
import chatService from '@/services/chat-service';
|
import chatService from '@/services/chat-service';
|
||||||
import kbService from '@/services/knowledge-service';
|
import kbService, { listDocument } from '@/services/knowledge-service';
|
||||||
import api, { api_host } from '@/utils/api';
|
import api, { api_host } from '@/utils/api';
|
||||||
import { buildChunkHighlights } from '@/utils/document-util';
|
import { buildChunkHighlights } from '@/utils/document-util';
|
||||||
import { post } from '@/utils/request';
|
import { post } from '@/utils/request';
|
||||||
@ -73,7 +73,7 @@ export const useFetchNextDocumentList = () => {
|
|||||||
refetchInterval: 15000,
|
refetchInterval: 15000,
|
||||||
enabled: !!knowledgeId || !!id,
|
enabled: !!knowledgeId || !!id,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const ret = await kbService.get_document_list({
|
const ret = await listDocument({
|
||||||
kb_id: knowledgeId || id,
|
kb_id: knowledgeId || id,
|
||||||
keywords: searchString,
|
keywords: searchString,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit';
|
||||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
import {
|
import {
|
||||||
IChangeParserConfigRequestBody,
|
IChangeParserConfigRequestBody,
|
||||||
IDocumentMetaRequestBody,
|
IDocumentMetaRequestBody,
|
||||||
} from '@/interfaces/request/document';
|
} from '@/interfaces/request/document';
|
||||||
import i18n from '@/locales/config';
|
import i18n from '@/locales/config';
|
||||||
import kbService from '@/services/knowledge-service';
|
import kbService, { listDocument } from '@/services/knowledge-service';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useDebounce } from 'ahooks';
|
import { useDebounce } from 'ahooks';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
@ -26,6 +27,7 @@ export const enum DocumentApiAction {
|
|||||||
SaveDocumentName = 'saveDocumentName',
|
SaveDocumentName = 'saveDocumentName',
|
||||||
SetDocumentParser = 'setDocumentParser',
|
SetDocumentParser = 'setDocumentParser',
|
||||||
SetDocumentMeta = 'setDocumentMeta',
|
SetDocumentMeta = 'setDocumentMeta',
|
||||||
|
FetchAllDocumentList = 'fetchAllDocumentList',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUploadNextDocument = () => {
|
export const useUploadNextDocument = () => {
|
||||||
@ -74,6 +76,7 @@ export const useFetchDocumentList = () => {
|
|||||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
|
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
|
||||||
|
const { filterValue, handleFilterSubmit } = useHandleFilterSubmit();
|
||||||
|
|
||||||
const { data, isFetching: loading } = useQuery<{
|
const { data, isFetching: loading } = useQuery<{
|
||||||
docs: IDocumentInfo[];
|
docs: IDocumentInfo[];
|
||||||
@ -83,17 +86,24 @@ export const useFetchDocumentList = () => {
|
|||||||
DocumentApiAction.FetchDocumentList,
|
DocumentApiAction.FetchDocumentList,
|
||||||
debouncedSearchString,
|
debouncedSearchString,
|
||||||
pagination,
|
pagination,
|
||||||
|
filterValue,
|
||||||
],
|
],
|
||||||
initialData: { docs: [], total: 0 },
|
initialData: { docs: [], total: 0 },
|
||||||
refetchInterval: 15000,
|
refetchInterval: 15000,
|
||||||
enabled: !!knowledgeId || !!id,
|
enabled: !!knowledgeId || !!id,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const ret = await kbService.get_document_list({
|
const ret = await listDocument(
|
||||||
kb_id: knowledgeId || id,
|
{
|
||||||
keywords: debouncedSearchString,
|
kb_id: knowledgeId || id,
|
||||||
page_size: pagination.pageSize,
|
keywords: debouncedSearchString,
|
||||||
page: pagination.current,
|
page_size: pagination.pageSize,
|
||||||
});
|
page: pagination.current,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
types: filterValue.type,
|
||||||
|
run_status: filterValue.run,
|
||||||
|
},
|
||||||
|
);
|
||||||
if (ret.data.code === 0) {
|
if (ret.data.code === 0) {
|
||||||
return ret.data.data;
|
return ret.data.data;
|
||||||
}
|
}
|
||||||
@ -120,9 +130,39 @@ export const useFetchDocumentList = () => {
|
|||||||
pagination: { ...pagination, total: data?.total },
|
pagination: { ...pagination, total: data?.total },
|
||||||
handleInputChange: onInputChange,
|
handleInputChange: onInputChange,
|
||||||
setPagination,
|
setPagination,
|
||||||
|
filterValue,
|
||||||
|
handleFilterSubmit,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function useFetchAllDocumentList() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const { data, isFetching: loading } = useQuery<{
|
||||||
|
docs: IDocumentInfo[];
|
||||||
|
total: number;
|
||||||
|
}>({
|
||||||
|
queryKey: [DocumentApiAction.FetchAllDocumentList],
|
||||||
|
initialData: { docs: [], total: 0 },
|
||||||
|
refetchInterval: 15000,
|
||||||
|
enabled: !!id,
|
||||||
|
queryFn: async () => {
|
||||||
|
const ret = await listDocument({
|
||||||
|
kb_id: id,
|
||||||
|
});
|
||||||
|
if (ret.data.code === 0) {
|
||||||
|
return ret.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
docs: [],
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading };
|
||||||
|
}
|
||||||
|
|
||||||
export const useSetDocumentStatus = () => {
|
export const useSetDocumentStatus = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit';
|
||||||
import {
|
import {
|
||||||
IKnowledge,
|
IKnowledge,
|
||||||
IKnowledgeResult,
|
IKnowledgeResult,
|
||||||
@ -72,8 +73,8 @@ export const useTestRetrieval = () => {
|
|||||||
export const useFetchNextKnowledgeListByPage = () => {
|
export const useFetchNextKnowledgeListByPage = () => {
|
||||||
const { searchString, handleInputChange } = useHandleSearchChange();
|
const { searchString, handleInputChange } = useHandleSearchChange();
|
||||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||||
const [ownerIds, setOwnerIds] = useState<string[]>([]);
|
|
||||||
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
|
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
|
||||||
|
const { filterValue, handleFilterSubmit } = useHandleFilterSubmit();
|
||||||
|
|
||||||
const { data, isFetching: loading } = useQuery<IKnowledgeResult>({
|
const { data, isFetching: loading } = useQuery<IKnowledgeResult>({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
@ -81,7 +82,7 @@ export const useFetchNextKnowledgeListByPage = () => {
|
|||||||
{
|
{
|
||||||
debouncedSearchString,
|
debouncedSearchString,
|
||||||
...pagination,
|
...pagination,
|
||||||
ownerIds,
|
filterValue,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
initialData: {
|
initialData: {
|
||||||
@ -97,7 +98,7 @@ export const useFetchNextKnowledgeListByPage = () => {
|
|||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
owner_ids: ownerIds,
|
owner_ids: filterValue.owner,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -113,11 +114,6 @@ export const useFetchNextKnowledgeListByPage = () => {
|
|||||||
[handleInputChange],
|
[handleInputChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOwnerIdsChange = useCallback((ids: string[]) => {
|
|
||||||
// setPagination({ page: 1 }); // TODO: 这里导致重复请求
|
|
||||||
setOwnerIds(ids);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
searchString,
|
searchString,
|
||||||
@ -125,8 +121,8 @@ export const useFetchNextKnowledgeListByPage = () => {
|
|||||||
pagination: { ...pagination, total: data?.total },
|
pagination: { ...pagination, total: data?.total },
|
||||||
setPagination,
|
setPagination,
|
||||||
loading,
|
loading,
|
||||||
setOwnerIds: handleOwnerIdsChange,
|
filterValue,
|
||||||
ownerIds,
|
handleFilterSubmit,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,7 +14,13 @@ export interface IFetchKnowledgeListRequestBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IFetchKnowledgeListRequestParams {
|
export interface IFetchKnowledgeListRequestParams {
|
||||||
|
kb_id?: string;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
page_size?: number;
|
page_size?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IFetchDocumentListRequestBody {
|
||||||
|
types?: string[];
|
||||||
|
run_status?: string[];
|
||||||
|
}
|
||||||
|
@ -35,14 +35,16 @@ import { useDatasetTableColumns } from './use-dataset-table-columns';
|
|||||||
import { useRenameDocument } from './use-rename-document';
|
import { useRenameDocument } from './use-rename-document';
|
||||||
import { useSaveMeta } from './use-save-meta';
|
import { useSaveMeta } from './use-save-meta';
|
||||||
|
|
||||||
export function DatasetTable() {
|
export type DatasetTableProps = Pick<
|
||||||
const {
|
ReturnType<typeof useFetchDocumentList>,
|
||||||
// searchString,
|
'documents' | 'setPagination' | 'pagination'
|
||||||
documents,
|
>;
|
||||||
pagination,
|
|
||||||
// handleInputChange,
|
export function DatasetTable({
|
||||||
setPagination,
|
documents,
|
||||||
} = useFetchDocumentList();
|
pagination,
|
||||||
|
setPagination,
|
||||||
|
}: DatasetTableProps) {
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
[],
|
[],
|
||||||
|
@ -2,9 +2,11 @@ import { BulkOperateBar } from '@/components/bulk-operate-bar';
|
|||||||
import { FileUploadDialog } from '@/components/file-upload-dialog';
|
import { FileUploadDialog } from '@/components/file-upload-dialog';
|
||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useFetchDocumentList } from '@/hooks/use-document-request';
|
||||||
import { Upload } from 'lucide-react';
|
import { Upload } from 'lucide-react';
|
||||||
import { DatasetTable } from './dataset-table';
|
import { DatasetTable } from './dataset-table';
|
||||||
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
|
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
|
||||||
|
import { useSelectDatasetFilters } from './use-select-filters';
|
||||||
import { useHandleUploadDocument } from './use-upload-document';
|
import { useHandleUploadDocument } from './use-upload-document';
|
||||||
|
|
||||||
export default function Dataset() {
|
export default function Dataset() {
|
||||||
@ -16,10 +18,27 @@ export default function Dataset() {
|
|||||||
documentUploadLoading,
|
documentUploadLoading,
|
||||||
} = useHandleUploadDocument();
|
} = useHandleUploadDocument();
|
||||||
const { list } = useBulkOperateDataset();
|
const { list } = useBulkOperateDataset();
|
||||||
|
const {
|
||||||
|
searchString,
|
||||||
|
documents,
|
||||||
|
pagination,
|
||||||
|
handleInputChange,
|
||||||
|
setPagination,
|
||||||
|
filterValue,
|
||||||
|
handleFilterSubmit,
|
||||||
|
} = useFetchDocumentList();
|
||||||
|
const { filters } = useSelectDatasetFilters();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-8">
|
<section className="p-8">
|
||||||
<ListFilterBar title="Dataset">
|
<ListFilterBar
|
||||||
|
title="Dataset"
|
||||||
|
onSearchChange={handleInputChange}
|
||||||
|
searchString={searchString}
|
||||||
|
value={filterValue}
|
||||||
|
onChange={handleFilterSubmit}
|
||||||
|
filters={filters}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant={'tertiary'}
|
variant={'tertiary'}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
@ -30,7 +49,11 @@ export default function Dataset() {
|
|||||||
</Button>
|
</Button>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
<BulkOperateBar list={list}></BulkOperateBar>
|
<BulkOperateBar list={list}></BulkOperateBar>
|
||||||
<DatasetTable></DatasetTable>
|
<DatasetTable
|
||||||
|
documents={documents}
|
||||||
|
pagination={pagination}
|
||||||
|
setPagination={setPagination}
|
||||||
|
></DatasetTable>
|
||||||
{documentUploadVisible && (
|
{documentUploadVisible && (
|
||||||
<FileUploadDialog
|
<FileUploadDialog
|
||||||
hideModal={hideDocumentUploadModal}
|
hideModal={hideDocumentUploadModal}
|
||||||
|
31
web/src/pages/dataset/dataset/use-select-filters.ts
Normal file
31
web/src/pages/dataset/dataset/use-select-filters.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { useFetchAllDocumentList } from '@/hooks/use-document-request';
|
||||||
|
import { groupListByType } from '@/utils/dataset-util';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export function useSelectDatasetFilters() {
|
||||||
|
const {
|
||||||
|
data: { docs: documents },
|
||||||
|
} = useFetchAllDocumentList();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const fileTypes = useMemo(() => {
|
||||||
|
return groupListByType(documents, 'type', 'type');
|
||||||
|
}, [documents]);
|
||||||
|
|
||||||
|
const fileStatus = useMemo(() => {
|
||||||
|
return groupListByType(documents, 'run', 'run').map((x) => ({
|
||||||
|
...x,
|
||||||
|
label: t(`knowledgeDetails.runningStatus${x.label}`),
|
||||||
|
}));
|
||||||
|
}, [documents, t]);
|
||||||
|
|
||||||
|
const filters = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{ field: 'type', label: 'File Type', list: fileTypes },
|
||||||
|
{ field: 'run', label: 'Status', list: fileStatus },
|
||||||
|
];
|
||||||
|
}, [fileStatus, fileTypes]);
|
||||||
|
|
||||||
|
return { fileTypes, fileStatus, filters };
|
||||||
|
}
|
@ -4,14 +4,14 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request';
|
import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { PropsWithChildren, useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DatasetCard } from './dataset-card';
|
import { DatasetCard } from './dataset-card';
|
||||||
import { DatasetCreatingDialog } from './dataset-creating-dialog';
|
import { DatasetCreatingDialog } from './dataset-creating-dialog';
|
||||||
import { DatasetsFilterPopover } from './datasets-filter-popover';
|
|
||||||
import { DatasetsPagination } from './datasets-pagination';
|
import { DatasetsPagination } from './datasets-pagination';
|
||||||
import { useSaveKnowledge } from './hooks';
|
import { useSaveKnowledge } from './hooks';
|
||||||
import { useRenameDataset } from './use-rename-dataset';
|
import { useRenameDataset } from './use-rename-dataset';
|
||||||
|
import { useSelectOwners } from './use-select-owners';
|
||||||
|
|
||||||
export default function Datasets() {
|
export default function Datasets() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -30,10 +30,12 @@ export default function Datasets() {
|
|||||||
setPagination,
|
setPagination,
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
searchString,
|
searchString,
|
||||||
setOwnerIds,
|
filterValue,
|
||||||
ownerIds,
|
handleFilterSubmit,
|
||||||
} = useFetchNextKnowledgeListByPage();
|
} = useFetchNextKnowledgeListByPage();
|
||||||
|
|
||||||
|
const owners = useSelectOwners();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
datasetRenameLoading,
|
datasetRenameLoading,
|
||||||
initialDatasetName,
|
initialDatasetName,
|
||||||
@ -54,14 +56,11 @@ export default function Datasets() {
|
|||||||
<section className="p-8 text-foreground">
|
<section className="p-8 text-foreground">
|
||||||
<ListFilterBar
|
<ListFilterBar
|
||||||
title="Datasets"
|
title="Datasets"
|
||||||
count={ownerIds.length}
|
|
||||||
FilterPopover={({ children }: PropsWithChildren) => (
|
|
||||||
<DatasetsFilterPopover setOwnerIds={setOwnerIds} ownerIds={ownerIds}>
|
|
||||||
{children}
|
|
||||||
</DatasetsFilterPopover>
|
|
||||||
)}
|
|
||||||
searchString={searchString}
|
searchString={searchString}
|
||||||
onSearchChange={handleInputChange}
|
onSearchChange={handleInputChange}
|
||||||
|
value={filterValue}
|
||||||
|
filters={owners}
|
||||||
|
onChange={handleFilterSubmit}
|
||||||
>
|
>
|
||||||
<Button variant={'tertiary'} size={'sm'} onClick={showModal}>
|
<Button variant={'tertiary'} size={'sm'} onClick={showModal}>
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
@ -1,28 +1,18 @@
|
|||||||
|
import { FilterCollection } from '@/components/list-filter-bar/interface';
|
||||||
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
||||||
|
import { groupListByType } from '@/utils/dataset-util';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
export type OwnerFilterType = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
count: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useSelectOwners() {
|
export function useSelectOwners() {
|
||||||
const { list } = useFetchKnowledgeList();
|
const { list } = useFetchKnowledgeList();
|
||||||
|
|
||||||
const owners = useMemo(() => {
|
const owners = useMemo(() => {
|
||||||
const ownerList: OwnerFilterType[] = [];
|
return groupListByType(list, 'tenant_id', 'nickname');
|
||||||
list.forEach((x) => {
|
|
||||||
const item = ownerList.find((y) => y.id === x.tenant_id);
|
|
||||||
if (!item) {
|
|
||||||
ownerList.push({ id: x.tenant_id, label: x.nickname, count: 1 });
|
|
||||||
} else {
|
|
||||||
item.count += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return ownerList;
|
|
||||||
}, [list]);
|
}, [list]);
|
||||||
|
|
||||||
return owners;
|
const filters: FilterCollection[] = [
|
||||||
|
{ field: 'owner', list: owners, label: 'Owner' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return filters;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { IRenameTag } from '@/interfaces/database/knowledge';
|
import { IRenameTag } from '@/interfaces/database/knowledge';
|
||||||
import {
|
import {
|
||||||
|
IFetchDocumentListRequestBody,
|
||||||
IFetchKnowledgeListRequestBody,
|
IFetchKnowledgeListRequestBody,
|
||||||
IFetchKnowledgeListRequestParams,
|
IFetchKnowledgeListRequestParams,
|
||||||
} from '@/interfaces/request/knowledge';
|
} from '@/interfaces/request/knowledge';
|
||||||
@ -182,4 +183,9 @@ export const listDataset = (
|
|||||||
body?: IFetchKnowledgeListRequestBody,
|
body?: IFetchKnowledgeListRequestBody,
|
||||||
) => request.post(api.kb_list, { data: body || {}, params });
|
) => request.post(api.kb_list, { data: body || {}, params });
|
||||||
|
|
||||||
|
export const listDocument = (
|
||||||
|
params?: IFetchKnowledgeListRequestParams,
|
||||||
|
body?: IFetchDocumentListRequestBody,
|
||||||
|
) => request.post(api.get_document_list, { data: body || {}, params });
|
||||||
|
|
||||||
export default kbService;
|
export default kbService;
|
||||||
|
@ -7,3 +7,27 @@ export function isKnowledgeGraphParser(parserId: DocumentParserType) {
|
|||||||
export function isNaiveParser(parserId: DocumentParserType) {
|
export function isNaiveParser(parserId: DocumentParserType) {
|
||||||
return parserId === DocumentParserType.Naive;
|
return parserId === DocumentParserType.Naive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FilterType = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function groupListByType<T extends Record<string, any>>(
|
||||||
|
list: T[],
|
||||||
|
idField: string,
|
||||||
|
labelField: string,
|
||||||
|
) {
|
||||||
|
const fileTypeList: FilterType[] = [];
|
||||||
|
list.forEach((x) => {
|
||||||
|
const item = fileTypeList.find((y) => y.id === x[idField]);
|
||||||
|
if (!item) {
|
||||||
|
fileTypeList.push({ id: x[idField], label: x[labelField], count: 1 });
|
||||||
|
} else {
|
||||||
|
item.count += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fileTypeList;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user