mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-13 01:49:01 +08:00
### What problem does this PR solve? Feat: Bind data to datasets page #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
17fa2e9e8e
commit
c1583a3e1d
128
web/src/components/chunk-method-dialog/hooks.ts
Normal file
128
web/src/components/chunk-method-dialog/hooks.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { useSelectParserList } from '@/hooks/user-setting-hooks';
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
const ParserListMap = new Map([
|
||||||
|
[
|
||||||
|
['pdf'],
|
||||||
|
[
|
||||||
|
'naive',
|
||||||
|
'resume',
|
||||||
|
'manual',
|
||||||
|
'paper',
|
||||||
|
'book',
|
||||||
|
'laws',
|
||||||
|
'presentation',
|
||||||
|
'one',
|
||||||
|
'qa',
|
||||||
|
'knowledge_graph',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['doc', 'docx'],
|
||||||
|
[
|
||||||
|
'naive',
|
||||||
|
'resume',
|
||||||
|
'book',
|
||||||
|
'laws',
|
||||||
|
'one',
|
||||||
|
'qa',
|
||||||
|
'manual',
|
||||||
|
'knowledge_graph',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['xlsx', 'xls'],
|
||||||
|
['naive', 'qa', 'table', 'one', 'knowledge_graph'],
|
||||||
|
],
|
||||||
|
[['ppt', 'pptx'], ['presentation']],
|
||||||
|
[
|
||||||
|
['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif', 'tiff', 'webp', 'svg', 'ico'],
|
||||||
|
['picture'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['txt'],
|
||||||
|
[
|
||||||
|
'naive',
|
||||||
|
'resume',
|
||||||
|
'book',
|
||||||
|
'laws',
|
||||||
|
'one',
|
||||||
|
'qa',
|
||||||
|
'table',
|
||||||
|
'knowledge_graph',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['csv'],
|
||||||
|
[
|
||||||
|
'naive',
|
||||||
|
'resume',
|
||||||
|
'book',
|
||||||
|
'laws',
|
||||||
|
'one',
|
||||||
|
'qa',
|
||||||
|
'table',
|
||||||
|
'knowledge_graph',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[['md'], ['naive', 'qa', 'knowledge_graph']],
|
||||||
|
[['json'], ['naive', 'knowledge_graph']],
|
||||||
|
[['eml'], ['email']],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getParserList = (
|
||||||
|
values: string[],
|
||||||
|
parserList: Array<{
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}>,
|
||||||
|
) => {
|
||||||
|
return parserList.filter((x) => values?.some((y) => y === x.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchParserListOnMount = (
|
||||||
|
documentId: string,
|
||||||
|
parserId: string,
|
||||||
|
documentExtension: string,
|
||||||
|
// form: FormInstance,
|
||||||
|
) => {
|
||||||
|
const [selectedTag, setSelectedTag] = useState('');
|
||||||
|
const parserList = useSelectParserList();
|
||||||
|
// const handleChunkMethodSelectChange = useHandleChunkMethodSelectChange(form); // TODO
|
||||||
|
|
||||||
|
const nextParserList = useMemo(() => {
|
||||||
|
const key = [...ParserListMap.keys()].find((x) =>
|
||||||
|
x.some((y) => y === documentExtension),
|
||||||
|
);
|
||||||
|
if (key) {
|
||||||
|
const values = ParserListMap.get(key);
|
||||||
|
return getParserList(values ?? [], parserList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getParserList(
|
||||||
|
['naive', 'resume', 'book', 'laws', 'one', 'qa', 'table'],
|
||||||
|
parserList,
|
||||||
|
);
|
||||||
|
}, [parserList, documentExtension]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedTag(parserId);
|
||||||
|
}, [parserId, documentId]);
|
||||||
|
|
||||||
|
const handleChange = (tag: string) => {
|
||||||
|
// handleChunkMethodSelectChange(tag);
|
||||||
|
setSelectedTag(tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { parserList: nextParserList, handleChange, selectedTag };
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideAutoKeywords = ['qa', 'table', 'resume', 'knowledge_graph', 'tag'];
|
||||||
|
|
||||||
|
export const useShowAutoKeywords = () => {
|
||||||
|
const showAutoKeywords = useCallback((selectedTag: string) => {
|
||||||
|
return hideAutoKeywords.every((x) => selectedTag !== x);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return showAutoKeywords;
|
||||||
|
};
|
127
web/src/components/chunk-method-dialog/index.tsx
Normal file
127
web/src/components/chunk-method-dialog/index.tsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/components/ui/form';
|
||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { IParserConfig } from '@/interfaces/database/document';
|
||||||
|
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '../ui/select';
|
||||||
|
import { useFetchParserListOnMount } from './hooks';
|
||||||
|
|
||||||
|
interface IProps
|
||||||
|
extends IModalProps<{
|
||||||
|
parserId: string;
|
||||||
|
parserConfig: IChangeParserConfigRequestBody;
|
||||||
|
}> {
|
||||||
|
loading: boolean;
|
||||||
|
parserId: string;
|
||||||
|
parserConfig: IParserConfig;
|
||||||
|
documentExtension: string;
|
||||||
|
documentId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChunkMethodDialog({
|
||||||
|
hideModal,
|
||||||
|
onOk,
|
||||||
|
parserId,
|
||||||
|
documentId,
|
||||||
|
documentExtension,
|
||||||
|
}: IProps) {
|
||||||
|
const { t } = useTranslate('knowledgeDetails');
|
||||||
|
|
||||||
|
const { parserList } = useFetchParserListOnMount(
|
||||||
|
documentId,
|
||||||
|
parserId,
|
||||||
|
documentExtension,
|
||||||
|
// form,
|
||||||
|
);
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, {
|
||||||
|
message: 'namePlaceholder',
|
||||||
|
})
|
||||||
|
.trim(),
|
||||||
|
});
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: { name: '' },
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
const ret = await onOk?.();
|
||||||
|
if (ret) {
|
||||||
|
hideModal?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onOpenChange={hideModal}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{t('chunkMethod')}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
{...field}
|
||||||
|
autoComplete="off"
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a verified email to display" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{parserList.map((x) => (
|
||||||
|
<SelectItem value={x.value} key={x.value}>
|
||||||
|
{x.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit">Save changes</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
@ -16,6 +16,7 @@ import { UploadFile, message } from 'antd';
|
|||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { IHighlight } from 'react-pdf-highlighter';
|
import { IHighlight } from 'react-pdf-highlighter';
|
||||||
|
import { useParams } from 'umi';
|
||||||
import {
|
import {
|
||||||
useGetPaginationWithRouter,
|
useGetPaginationWithRouter,
|
||||||
useHandleSearchChange,
|
useHandleSearchChange,
|
||||||
@ -61,6 +62,7 @@ export const useFetchNextDocumentList = () => {
|
|||||||
const { knowledgeId } = useGetKnowledgeSearchParams();
|
const { knowledgeId } = useGetKnowledgeSearchParams();
|
||||||
const { searchString, handleInputChange } = useHandleSearchChange();
|
const { searchString, handleInputChange } = useHandleSearchChange();
|
||||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
const { data, isFetching: loading } = useQuery<{
|
const { data, isFetching: loading } = useQuery<{
|
||||||
docs: IDocumentInfo[];
|
docs: IDocumentInfo[];
|
||||||
@ -69,9 +71,10 @@ export const useFetchNextDocumentList = () => {
|
|||||||
queryKey: ['fetchDocumentList', searchString, pagination],
|
queryKey: ['fetchDocumentList', searchString, pagination],
|
||||||
initialData: { docs: [], total: 0 },
|
initialData: { docs: [], total: 0 },
|
||||||
refetchInterval: 15000,
|
refetchInterval: 15000,
|
||||||
|
enabled: !!knowledgeId || !!id,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const ret = await kbService.get_document_list({
|
const ret = await kbService.get_document_list({
|
||||||
kb_id: knowledgeId,
|
kb_id: knowledgeId || id,
|
||||||
keywords: searchString,
|
keywords: searchString,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
|
@ -9,9 +9,12 @@ export const useNavigatePage = () => {
|
|||||||
navigate(Routes.Datasets);
|
navigate(Routes.Datasets);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
const navigateToDataset = useCallback(() => {
|
const navigateToDataset = useCallback(
|
||||||
navigate(Routes.Dataset);
|
(id: string) => () => {
|
||||||
}, [navigate]);
|
navigate(`${Routes.Dataset}/${id}`);
|
||||||
|
},
|
||||||
|
[navigate],
|
||||||
|
);
|
||||||
|
|
||||||
const navigateToHome = useCallback(() => {
|
const navigateToHome = useCallback(() => {
|
||||||
navigate(Routes.Home);
|
navigate(Routes.Home);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
SortingState,
|
SortingState,
|
||||||
VisibilityState,
|
VisibilityState,
|
||||||
@ -12,20 +11,10 @@ import {
|
|||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from '@tanstack/react-table';
|
} from '@tanstack/react-table';
|
||||||
import { ArrowUpDown, MoreHorizontal, Pencil } from 'lucide-react';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { ChunkMethodDialog } from '@/components/chunk-method-dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import { Switch } from '@/components/ui/switch';
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@ -34,43 +23,22 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from '@/components/ui/table';
|
} from '@/components/ui/table';
|
||||||
import { RunningStatus } from '@/constants/knowledge';
|
import { useFetchNextDocumentList } from '@/hooks/document-hooks';
|
||||||
|
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
||||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { getExtension } from '@/utils/document-util';
|
||||||
|
import { useMemo } from 'react';
|
||||||
const data: IDocumentInfo[] = [
|
import { useChangeDocumentParser } from './hooks';
|
||||||
{
|
import { useDatasetTableColumns } from './use-dataset-table-columns';
|
||||||
chunk_num: 1,
|
|
||||||
create_date: 'Thu, 28 Nov 2024 17:10:22 GMT',
|
|
||||||
create_time: 1732785022792,
|
|
||||||
created_by: 'b0975cb4bc3111ee9b830aef05f5e94f',
|
|
||||||
id: '990cb30ead6811efb9b9fa163e197198',
|
|
||||||
kb_id: '25a8cfbe9cd411efbc12fa163e197198',
|
|
||||||
location: 'mian.jpg',
|
|
||||||
name: 'mian.jpg',
|
|
||||||
parser_config: {
|
|
||||||
pages: [[1, 1000000]],
|
|
||||||
},
|
|
||||||
parser_id: 'picture',
|
|
||||||
process_begin_at: 'Thu, 28 Nov 2024 17:10:25 GMT',
|
|
||||||
process_duation: 8.46185,
|
|
||||||
progress: 1,
|
|
||||||
progress_msg:
|
|
||||||
'\nTask has been received.\nPage(1~100000001): Finish OCR: (用小麦粉\n金\nONGXI ...)\nPage(1~100000001): OCR results is too long to use CV LLM.\nPage(1~100000001): Finished slicing files (1 chunks in 0.34s). Start to embedding the content.\nPage(1~100000001): Finished embedding (in 0.35s)! Start to build index!\nPage(1~100000001): Indexing elapsed in 0.02s.\nPage(1~100000001): Done!',
|
|
||||||
run: RunningStatus.RUNNING,
|
|
||||||
size: 19692,
|
|
||||||
source_type: 'local',
|
|
||||||
status: '1',
|
|
||||||
thumbnail:
|
|
||||||
'/v1/document/image/25a8cfbe9cd411efbc12fa163e197198-thumbnail_990cb30ead6811efb9b9fa163e197198.png',
|
|
||||||
token_num: 115,
|
|
||||||
type: 'visual',
|
|
||||||
update_date: 'Thu, 28 Nov 2024 17:10:33 GMT',
|
|
||||||
update_time: 1732785033462,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function DatasetTable() {
|
export function DatasetTable() {
|
||||||
|
const {
|
||||||
|
// searchString,
|
||||||
|
documents,
|
||||||
|
pagination,
|
||||||
|
// handleInputChange,
|
||||||
|
setPagination,
|
||||||
|
} = useFetchNextDocumentList();
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
[],
|
[],
|
||||||
@ -78,122 +46,31 @@ export function DatasetTable() {
|
|||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
const { t } = useTranslation('translation', {
|
|
||||||
keyPrefix: 'knowledgeDetails',
|
const { currentRecord, setRecord } = useSetSelectedRecord<IDocumentInfo>();
|
||||||
|
|
||||||
|
const {
|
||||||
|
changeParserLoading,
|
||||||
|
onChangeParserOk,
|
||||||
|
changeParserVisible,
|
||||||
|
hideChangeParserModal,
|
||||||
|
showChangeParserModal,
|
||||||
|
} = useChangeDocumentParser(currentRecord.id);
|
||||||
|
|
||||||
|
const columns = useDatasetTableColumns({
|
||||||
|
showChangeParserModal,
|
||||||
|
setCurrentRecord: setRecord,
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns: ColumnDef<IDocumentInfo>[] = [
|
const currentPagination = useMemo(() => {
|
||||||
{
|
return {
|
||||||
id: 'select',
|
pageIndex: (pagination.current || 1) - 1,
|
||||||
header: ({ table }) => (
|
pageSize: pagination.pageSize || 10,
|
||||||
<Checkbox
|
};
|
||||||
checked={
|
}, [pagination]);
|
||||||
table.getIsAllPageRowsSelected() ||
|
|
||||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
|
||||||
}
|
|
||||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
||||||
aria-label="Select all"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Checkbox
|
|
||||||
checked={row.getIsSelected()}
|
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
||||||
aria-label="Select row"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
enableSorting: false,
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'name',
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
||||||
>
|
|
||||||
{t('name')}
|
|
||||||
<ArrowUpDown />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="capitalize">{row.getValue('name')}</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'create_time',
|
|
||||||
header: ({ column }) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
||||||
>
|
|
||||||
{t('uploadDate')}
|
|
||||||
<ArrowUpDown />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="lowercase">{row.getValue('create_time')}</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'parser_id',
|
|
||||||
header: t('chunkMethod'),
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="capitalize">{row.getValue('parser_id')}</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'run',
|
|
||||||
header: t('parsingStatus'),
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Button variant="destructive" size={'sm'}>
|
|
||||||
{row.getValue('run')}
|
|
||||||
</Button>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'actions',
|
|
||||||
header: t('action'),
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const payment = row.original;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="flex gap-4 items-center">
|
|
||||||
<Switch id="airplane-mode" />
|
|
||||||
<Button variant="secondary" size={'icon'}>
|
|
||||||
<Pencil />
|
|
||||||
</Button>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="secondary" size={'icon'}>
|
|
||||||
<MoreHorizontal />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end">
|
|
||||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
|
||||||
>
|
|
||||||
Copy payment ID
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data: documents,
|
||||||
columns,
|
columns,
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
onColumnFiltersChange: setColumnFilters,
|
onColumnFiltersChange: setColumnFilters,
|
||||||
@ -203,12 +80,29 @@ export function DatasetTable() {
|
|||||||
getFilteredRowModel: getFilteredRowModel(),
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
onRowSelectionChange: setRowSelection,
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: (updaterOrValue: any) => {
|
||||||
|
if (typeof updaterOrValue === 'function') {
|
||||||
|
const nextPagination = updaterOrValue(currentPagination);
|
||||||
|
setPagination({
|
||||||
|
page: nextPagination.pageIndex + 1,
|
||||||
|
pageSize: nextPagination.pageSize,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setPagination({
|
||||||
|
page: updaterOrValue.pageIndex,
|
||||||
|
pageSize: updaterOrValue.pageSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
manualPagination: true, //we're doing manual "server-side" pagination
|
||||||
state: {
|
state: {
|
||||||
sorting,
|
sorting,
|
||||||
columnFilters,
|
columnFilters,
|
||||||
columnVisibility,
|
columnVisibility,
|
||||||
rowSelection,
|
rowSelection,
|
||||||
|
pagination: currentPagination,
|
||||||
},
|
},
|
||||||
|
rowCount: pagination.total ?? 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -241,7 +135,10 @@ export function DatasetTable() {
|
|||||||
data-state={row.getIsSelected() && 'selected'}
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell key={cell.id}>
|
<TableCell
|
||||||
|
key={cell.id}
|
||||||
|
className={cell.column.columnDef.meta?.cellClassName}
|
||||||
|
>
|
||||||
{flexRender(
|
{flexRender(
|
||||||
cell.column.columnDef.cell,
|
cell.column.columnDef.cell,
|
||||||
cell.getContext(),
|
cell.getContext(),
|
||||||
@ -266,7 +163,7 @@ export function DatasetTable() {
|
|||||||
<div className="flex items-center justify-end space-x-2 py-4">
|
<div className="flex items-center justify-end space-x-2 py-4">
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
<div className="flex-1 text-sm text-muted-foreground">
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{' '}
|
{table.getFilteredSelectedRowModel().rows.length} of{' '}
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
{pagination?.total} row(s) selected.
|
||||||
</div>
|
</div>
|
||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
<Button
|
<Button
|
||||||
@ -287,6 +184,18 @@ export function DatasetTable() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{changeParserVisible && (
|
||||||
|
<ChunkMethodDialog
|
||||||
|
documentId={currentRecord.id}
|
||||||
|
parserId={currentRecord.parser_id}
|
||||||
|
parserConfig={currentRecord.parser_config}
|
||||||
|
documentExtension={getExtension(currentRecord.name)}
|
||||||
|
onOk={onChangeParserOk}
|
||||||
|
visible={changeParserVisible}
|
||||||
|
hideModal={hideChangeParserModal}
|
||||||
|
loading={changeParserLoading}
|
||||||
|
></ChunkMethodDialog>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,13 @@ export const useChangeDocumentParser = (documentId: string) => {
|
|||||||
} = useSetModalState();
|
} = useSetModalState();
|
||||||
|
|
||||||
const onChangeParserOk = useCallback(
|
const onChangeParserOk = useCallback(
|
||||||
async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => {
|
async ({
|
||||||
|
parserId,
|
||||||
|
parserConfig,
|
||||||
|
}: {
|
||||||
|
parserId: string;
|
||||||
|
parserConfig: IChangeParserConfigRequestBody;
|
||||||
|
}) => {
|
||||||
const ret = await setDocumentParser({
|
const ret = await setDocumentParser({
|
||||||
parserId,
|
parserId,
|
||||||
documentId,
|
documentId,
|
||||||
|
194
web/src/pages/dataset/dataset/use-dataset-table-columns.tsx
Normal file
194
web/src/pages/dataset/dataset/use-dataset-table-columns.tsx
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import SvgIcon from '@/components/svg-icon';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from '@/components/ui/tooltip';
|
||||||
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { getExtension } from '@/utils/document-util';
|
||||||
|
import { ColumnDef } from '@tanstack/table-core';
|
||||||
|
import { ArrowUpDown, MoreHorizontal, Pencil, Wrench } from 'lucide-react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useChangeDocumentParser } from './hooks';
|
||||||
|
|
||||||
|
type UseDatasetTableColumnsType = Pick<
|
||||||
|
ReturnType<typeof useChangeDocumentParser>,
|
||||||
|
'showChangeParserModal'
|
||||||
|
> & { setCurrentRecord: (record: IDocumentInfo) => void };
|
||||||
|
|
||||||
|
export function useDatasetTableColumns({
|
||||||
|
showChangeParserModal,
|
||||||
|
setCurrentRecord,
|
||||||
|
}: UseDatasetTableColumnsType) {
|
||||||
|
const { t } = useTranslation('translation', {
|
||||||
|
keyPrefix: 'knowledgeDetails',
|
||||||
|
});
|
||||||
|
|
||||||
|
// const onShowRenameModal = (record: IDocumentInfo) => {
|
||||||
|
// setCurrentRecord(record);
|
||||||
|
// showRenameModal();
|
||||||
|
// };
|
||||||
|
const onShowChangeParserModal = useCallback(
|
||||||
|
(record: IDocumentInfo) => () => {
|
||||||
|
setCurrentRecord(record);
|
||||||
|
showChangeParserModal();
|
||||||
|
},
|
||||||
|
[setCurrentRecord, showChangeParserModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
// const onShowSetMetaModal = useCallback(() => {
|
||||||
|
// setRecord();
|
||||||
|
// showSetMetaModal();
|
||||||
|
// }, [setRecord, showSetMetaModal]);
|
||||||
|
|
||||||
|
const columns: ColumnDef<IDocumentInfo>[] = [
|
||||||
|
{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
table.getIsAllPageRowsSelected() ||
|
||||||
|
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
aria-label="Select all"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
aria-label="Select row"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'name',
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
>
|
||||||
|
{t('name')}
|
||||||
|
<ArrowUpDown />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
meta: { cellClassName: 'max-w-[20vw]' },
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const name: string = row.getValue('name');
|
||||||
|
// return <div className="capitalize">{row.getValue('name')}</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<SvgIcon
|
||||||
|
name={`file-icon/${getExtension(name)}`}
|
||||||
|
width={24}
|
||||||
|
></SvgIcon>
|
||||||
|
<span className={cn('truncate')}>{name}</span>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{name}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'create_time',
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
|
>
|
||||||
|
{t('uploadDate')}
|
||||||
|
<ArrowUpDown />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="lowercase">{row.getValue('create_time')}</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'parser_id',
|
||||||
|
header: t('chunkMethod'),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="capitalize">{row.getValue('parser_id')}</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'run',
|
||||||
|
header: t('parsingStatus'),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Button variant="destructive" size={'sm'}>
|
||||||
|
{row.getValue('run')}
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
header: t('action'),
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const record = row.original;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="flex gap-4 items-center">
|
||||||
|
<Switch id="airplane-mode" />
|
||||||
|
<Button
|
||||||
|
variant="icon"
|
||||||
|
size={'icon'}
|
||||||
|
onClick={onShowChangeParserModal(record)}
|
||||||
|
>
|
||||||
|
<Wrench />
|
||||||
|
</Button>
|
||||||
|
<Button variant="icon" size={'icon'}>
|
||||||
|
<Pencil />
|
||||||
|
</Button>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="icon" size={'icon'}>
|
||||||
|
<MoreHorizontal />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => navigator.clipboard.writeText(record.id)}
|
||||||
|
>
|
||||||
|
Copy payment ID
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
@ -1,87 +1,17 @@
|
|||||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import { useInfiniteFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
||||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
|
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||||
|
import { formatDate } from '@/utils/date';
|
||||||
import { ChevronRight, Plus, Trash2 } from 'lucide-react';
|
import { ChevronRight, Plus, Trash2 } from 'lucide-react';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import { DatasetCreatingDialog } from './dataset-creating-dialog';
|
import { DatasetCreatingDialog } from './dataset-creating-dialog';
|
||||||
import { useSaveKnowledge } from './hooks';
|
import { useSaveKnowledge } from './hooks';
|
||||||
|
|
||||||
const datasets = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: 'HR knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: 'IT knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
title: 'Legal knowledge base',
|
|
||||||
files: '1,242 files',
|
|
||||||
size: '152 MB',
|
|
||||||
created: '12.02.2024',
|
|
||||||
image: 'https://github.com/shadcn.png',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function Datasets() {
|
export default function Datasets() {
|
||||||
const {
|
const {
|
||||||
visible,
|
visible,
|
||||||
@ -92,6 +22,25 @@ export default function Datasets() {
|
|||||||
} = useSaveKnowledge();
|
} = useSaveKnowledge();
|
||||||
const { navigateToDataset } = useNavigatePage();
|
const { navigateToDataset } = useNavigatePage();
|
||||||
|
|
||||||
|
const {
|
||||||
|
fetchNextPage,
|
||||||
|
data,
|
||||||
|
hasNextPage,
|
||||||
|
searchString,
|
||||||
|
handleInputChange,
|
||||||
|
loading,
|
||||||
|
} = useInfiniteFetchKnowledgeList();
|
||||||
|
|
||||||
|
const nextList: IKnowledge[] = useMemo(() => {
|
||||||
|
const list =
|
||||||
|
data?.pages?.flatMap((x) => (Array.isArray(x.kbs) ? x.kbs : [])) ?? [];
|
||||||
|
return list;
|
||||||
|
}, [data?.pages]);
|
||||||
|
|
||||||
|
const total = useMemo(() => {
|
||||||
|
return data?.pages.at(-1).total ?? 0;
|
||||||
|
}, [data?.pages]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-8 text-foreground">
|
<section className="p-8 text-foreground">
|
||||||
<ListFilterBar title="Datasets" showDialog={showModal}>
|
<ListFilterBar title="Datasets" showDialog={showModal}>
|
||||||
@ -99,17 +48,17 @@ export default function Datasets() {
|
|||||||
Create dataset
|
Create dataset
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
||||||
{datasets.map((dataset) => (
|
{nextList.map((dataset) => (
|
||||||
<Card
|
<Card
|
||||||
key={dataset.id}
|
key={dataset.id}
|
||||||
className="bg-colors-background-inverse-weak flex-1"
|
className="bg-colors-background-inverse-weak flex-1"
|
||||||
>
|
>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<div className="flex justify-between mb-4">
|
<div className="flex justify-between mb-4">
|
||||||
<div
|
<Avatar className="w-[70px] h-[70px] rounded-lg">
|
||||||
className="w-[70px] h-[70px] rounded-xl bg-cover"
|
<AvatarImage src={dataset.avatar} />
|
||||||
style={{ backgroundImage: `url(${dataset.image})` }}
|
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||||
/>
|
</Avatar>
|
||||||
<ConfirmDeleteDialog>
|
<ConfirmDeleteDialog>
|
||||||
<Button variant="ghost" size="icon">
|
<Button variant="ghost" size="icon">
|
||||||
<Trash2 />
|
<Trash2 />
|
||||||
@ -118,20 +67,16 @@ export default function Datasets() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-end">
|
<div className="flex justify-between items-end">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-semibold mb-2">
|
<h3 className="text-lg font-semibold mb-2">{dataset.name}</h3>
|
||||||
{dataset.title}
|
<p className="text-sm opacity-80">{dataset.doc_num} files</p>
|
||||||
</h3>
|
|
||||||
<p className="text-sm opacity-80">
|
<p className="text-sm opacity-80">
|
||||||
{dataset.files} | {dataset.size}
|
Created {formatDate(dataset.update_time)}
|
||||||
</p>
|
|
||||||
<p className="text-sm opacity-80">
|
|
||||||
Created {dataset.created}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="icon"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={navigateToDataset}
|
onClick={navigateToDataset(dataset.id)}
|
||||||
>
|
>
|
||||||
<ChevronRight className="h-6 w-6" />
|
<ChevronRight className="h-6 w-6" />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -58,7 +58,7 @@ export function Datasets() {
|
|||||||
<Button
|
<Button
|
||||||
variant="icon"
|
variant="icon"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={navigateToDataset}
|
onClick={navigateToDataset(dataset.id)}
|
||||||
>
|
>
|
||||||
<ChevronRight className="h-6 w-6" />
|
<ChevronRight className="h-6 w-6" />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -222,7 +222,7 @@ const routes = [
|
|||||||
component: `@/pages${Routes.DatasetBase}`,
|
component: `@/pages${Routes.DatasetBase}`,
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: Routes.Dataset,
|
path: `${Routes.Dataset}/:id`,
|
||||||
component: `@/pages${Routes.Dataset}`,
|
component: `@/pages${Routes.Dataset}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user