mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-21 05:16:54 +08:00
Feature: Implement metadata functionality (#12049)
### What problem does this PR solve? Feature: Implement metadata functionality ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -691,7 +691,7 @@ const DynamicForm = {
|
|||||||
useImperativeHandle(
|
useImperativeHandle(
|
||||||
ref,
|
ref,
|
||||||
() => ({
|
() => ({
|
||||||
submit: form.handleSubmit,
|
submit: form.handleSubmit(onSubmit),
|
||||||
getValues: form.getValues,
|
getValues: form.getValues,
|
||||||
reset: (values?: T) => {
|
reset: (values?: T) => {
|
||||||
if (values) {
|
if (values) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { X } from 'lucide-react';
|
import { Trash2 } from 'lucide-react';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import {
|
import {
|
||||||
HoverCard,
|
HoverCard,
|
||||||
@ -57,14 +57,15 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
|||||||
<HoverCard key={tag}>
|
<HoverCard key={tag}>
|
||||||
<HoverCardContent side="top">{tag}</HoverCardContent>
|
<HoverCardContent side="top">{tag}</HoverCardContent>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<div className="w-fit flex items-center justify-center gap-2 border-dashed border px-2 py-1 rounded-sm bg-bg-card">
|
<div className="w-fit flex items-center justify-center gap-2 border border-border-button px-2 py-1 rounded-sm bg-bg-card">
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<div className="max-w-80 overflow-hidden text-ellipsis">
|
<div className="max-w-80 overflow-hidden text-ellipsis">
|
||||||
{tag}
|
{tag}
|
||||||
</div>
|
</div>
|
||||||
{!disabled && (
|
{!disabled && (
|
||||||
<X
|
<Trash2
|
||||||
className="w-4 h-4 text-muted-foreground hover:text-primary"
|
size={14}
|
||||||
|
className="text-text-secondary hover:text-state-error"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleClose(tag);
|
handleClose(tag);
|
||||||
@ -80,10 +81,6 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
|||||||
|
|
||||||
const tagChild = value?.map(forMap);
|
const tagChild = value?.map(forMap);
|
||||||
|
|
||||||
const tagPlusStyle: React.CSSProperties = {
|
|
||||||
borderStyle: 'dashed',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{inputVisible && (
|
{inputVisible && (
|
||||||
@ -102,15 +99,14 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-2 py-1">
|
<div className="flex gap-2 py-1 flex-wrap">
|
||||||
{Array.isArray(tagChild) && tagChild.length > 0 && <>{tagChild}</>}
|
{Array.isArray(tagChild) && tagChild.length > 0 && <>{tagChild}</>}
|
||||||
{!inputVisible && !disabled && (
|
{!inputVisible && !disabled && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="w-fit flex items-center justify-center gap-2 bg-bg-card border-dashed border"
|
className="w-fit flex items-center justify-center gap-2 bg-bg-card border-border-button border"
|
||||||
onClick={showInput}
|
onClick={showInput}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
style={tagPlusStyle}
|
|
||||||
>
|
>
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -175,6 +175,21 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
|
|||||||
parserRequired: 'Chunk method is required',
|
parserRequired: 'Chunk method is required',
|
||||||
},
|
},
|
||||||
knowledgeDetails: {
|
knowledgeDetails: {
|
||||||
|
metadata: {
|
||||||
|
changesAffectNewParses: 'Changes affect new parses only.',
|
||||||
|
editMetadataForDataset: 'View and edit metadata for ',
|
||||||
|
restrictDefinedValues: 'Restrict to defined values',
|
||||||
|
metadataGenerationSettings: 'Metadata generation settings',
|
||||||
|
manageMetadataForDataset: 'Manage metadata for this dataset',
|
||||||
|
manageMetadata: 'Manage metadata',
|
||||||
|
metadata: 'Metadata',
|
||||||
|
values: 'Values',
|
||||||
|
action: 'Action',
|
||||||
|
field: 'Field',
|
||||||
|
description: 'Description',
|
||||||
|
fieldName: 'Field name',
|
||||||
|
editMetadata: 'Edit metadata',
|
||||||
|
},
|
||||||
localUpload: 'Local upload',
|
localUpload: 'Local upload',
|
||||||
fileSize: 'File size',
|
fileSize: 'File size',
|
||||||
fileType: 'File type',
|
fileType: 'File type',
|
||||||
@ -348,6 +363,8 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
|
|||||||
reRankModelWaring: 'Re-rank model is very time consuming.',
|
reRankModelWaring: 'Re-rank model is very time consuming.',
|
||||||
},
|
},
|
||||||
knowledgeConfiguration: {
|
knowledgeConfiguration: {
|
||||||
|
settings: 'Settings',
|
||||||
|
autoMetadata: 'Auto metadata',
|
||||||
mineruOptions: 'MinerU Options',
|
mineruOptions: 'MinerU Options',
|
||||||
mineruParseMethod: 'Parse Method',
|
mineruParseMethod: 'Parse Method',
|
||||||
mineruParseMethodTip:
|
mineruParseMethodTip:
|
||||||
|
|||||||
@ -267,6 +267,8 @@ export default {
|
|||||||
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
|
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
|
||||||
},
|
},
|
||||||
knowledgeConfiguration: {
|
knowledgeConfiguration: {
|
||||||
|
settings: '设置',
|
||||||
|
autoMetadata: '自动元数据',
|
||||||
mineruOptions: 'MinerU 选项',
|
mineruOptions: 'MinerU 选项',
|
||||||
mineruParseMethod: '解析方法',
|
mineruParseMethod: '解析方法',
|
||||||
mineruParseMethodTip:
|
mineruParseMethodTip:
|
||||||
|
|||||||
370
web/src/pages/dataset/components/metedata/hook.ts
Normal file
370
web/src/pages/dataset/components/metedata/hook.ts
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
import message from '@/components/ui/message';
|
||||||
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
|
import { useSetDocumentMeta } from '@/hooks/use-document-request';
|
||||||
|
import {
|
||||||
|
getMetaDataService,
|
||||||
|
updateMetaData,
|
||||||
|
} from '@/services/knowledge-service';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useParams } from 'umi';
|
||||||
|
import {
|
||||||
|
IMetaDataReturnJSONSettings,
|
||||||
|
IMetaDataReturnJSONType,
|
||||||
|
IMetaDataReturnType,
|
||||||
|
IMetaDataTableData,
|
||||||
|
MetadataOperations,
|
||||||
|
ShowManageMetadataModalProps,
|
||||||
|
} from './interface';
|
||||||
|
export enum MetadataType {
|
||||||
|
Manage = 1,
|
||||||
|
UpdateSingle = 2,
|
||||||
|
Setting = 3,
|
||||||
|
}
|
||||||
|
export const util = {
|
||||||
|
changeToMetaDataTableData(data: IMetaDataReturnType): IMetaDataTableData[] {
|
||||||
|
return Object.entries(data).map(([key, value]) => {
|
||||||
|
const values = value.map(([v]) => v.toString());
|
||||||
|
console.log('values', values);
|
||||||
|
return {
|
||||||
|
field: key,
|
||||||
|
description: '',
|
||||||
|
values: values,
|
||||||
|
} as IMetaDataTableData;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
JSONToMetaDataTableData(
|
||||||
|
data: Record<string, string | string[]>,
|
||||||
|
): IMetaDataTableData[] {
|
||||||
|
return Object.entries(data).map(([key, value]) => {
|
||||||
|
return {
|
||||||
|
field: key,
|
||||||
|
description: '',
|
||||||
|
values: value,
|
||||||
|
} as IMetaDataTableData;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
tableDataToMetaDataJSON(data: IMetaDataTableData[]): IMetaDataReturnJSONType {
|
||||||
|
return data.reduce<IMetaDataReturnJSONType>((pre, cur) => {
|
||||||
|
pre[cur.field] = cur.values;
|
||||||
|
return pre;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
tableDataToMetaDataSettingJSON(
|
||||||
|
data: IMetaDataTableData[],
|
||||||
|
): IMetaDataReturnJSONSettings {
|
||||||
|
return data.map((item) => {
|
||||||
|
return {
|
||||||
|
key: item.field,
|
||||||
|
description: item.description,
|
||||||
|
enum: item.values,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
metaDataSettingJSONToMetaDataTableData(
|
||||||
|
data: IMetaDataReturnJSONSettings,
|
||||||
|
): IMetaDataTableData[] {
|
||||||
|
return data.map((item) => {
|
||||||
|
return {
|
||||||
|
field: item.key,
|
||||||
|
description: item.description,
|
||||||
|
values: item.enum,
|
||||||
|
restrictDefinedValues: !!item.enum?.length,
|
||||||
|
} as IMetaDataTableData;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMetadataOperations = () => {
|
||||||
|
const [operations, setOperations] = useState<MetadataOperations>({
|
||||||
|
deletes: [],
|
||||||
|
updates: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const addDeleteRow = useCallback((key: string) => {
|
||||||
|
setOperations((prev) => ({
|
||||||
|
...prev,
|
||||||
|
deletes: [...prev.deletes, { key }],
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addDeleteValue = useCallback((key: string, value: string) => {
|
||||||
|
setOperations((prev) => ({
|
||||||
|
...prev,
|
||||||
|
deletes: [...prev.deletes, { key, value }],
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addUpdateValue = useCallback(
|
||||||
|
(key: string, value: string | string[]) => {
|
||||||
|
setOperations((prev) => ({
|
||||||
|
...prev,
|
||||||
|
updates: [...prev.updates, { key, value }],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const resetOperations = useCallback(() => {
|
||||||
|
setOperations({
|
||||||
|
deletes: [],
|
||||||
|
updates: [],
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
operations,
|
||||||
|
addDeleteRow,
|
||||||
|
addDeleteValue,
|
||||||
|
addUpdateValue,
|
||||||
|
resetOperations,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchMetaDataManageData = (
|
||||||
|
type: MetadataType = MetadataType.Manage,
|
||||||
|
) => {
|
||||||
|
const { id } = useParams();
|
||||||
|
// const [data, setData] = useState<IMetaDataTableData[]>([]);
|
||||||
|
// const [loading, setLoading] = useState(false);
|
||||||
|
// const fetchData = useCallback(async (): Promise<IMetaDataTableData[]> => {
|
||||||
|
// setLoading(true);
|
||||||
|
// const { data } = await getMetaDataService({
|
||||||
|
// kb_id: id as string,
|
||||||
|
// });
|
||||||
|
// setLoading(false);
|
||||||
|
// if (data?.data?.summary) {
|
||||||
|
// return util.changeToMetaDataTableData(data.data.summary);
|
||||||
|
// }
|
||||||
|
// return [];
|
||||||
|
// }, [id]);
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (type === MetadataType.Manage) {
|
||||||
|
// fetchData()
|
||||||
|
// .then((res) => {
|
||||||
|
// setData(res);
|
||||||
|
// })
|
||||||
|
// .catch((res) => {
|
||||||
|
// console.error(res);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }, [type, fetchData]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<IMetaDataTableData[]>({
|
||||||
|
queryKey: ['fetchMetaData', id],
|
||||||
|
enabled: !!id && type === MetadataType.Manage,
|
||||||
|
initialData: [],
|
||||||
|
gcTime: 1000,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await getMetaDataService({
|
||||||
|
kb_id: id as string,
|
||||||
|
});
|
||||||
|
if (data?.data?.summary) {
|
||||||
|
return util.changeToMetaDataTableData(data.data.summary);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useManageMetaDataModal = (
|
||||||
|
metaData: IMetaDataTableData[] = [],
|
||||||
|
type: MetadataType = MetadataType.Manage,
|
||||||
|
otherData?: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { data, loading } = useFetchMetaDataManageData(type);
|
||||||
|
|
||||||
|
const [tableData, setTableData] = useState<IMetaDataTableData[]>(metaData);
|
||||||
|
|
||||||
|
const { operations, addDeleteRow, addDeleteValue, addUpdateValue } =
|
||||||
|
useMetadataOperations();
|
||||||
|
|
||||||
|
const { setDocumentMeta } = useSetDocumentMeta();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
setTableData(data);
|
||||||
|
} else {
|
||||||
|
setTableData([]);
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (metaData) {
|
||||||
|
setTableData(metaData);
|
||||||
|
} else {
|
||||||
|
setTableData([]);
|
||||||
|
}
|
||||||
|
}, [metaData]);
|
||||||
|
|
||||||
|
const handleDeleteSingleValue = useCallback(
|
||||||
|
(field: string, value: string) => {
|
||||||
|
addDeleteValue(field, value);
|
||||||
|
|
||||||
|
setTableData((prevTableData) => {
|
||||||
|
const newTableData = prevTableData.map((item) => {
|
||||||
|
if (item.field === field) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
values: item.values.filter((v) => v !== value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
// console.log('newTableData', newTableData, prevTableData);
|
||||||
|
return newTableData;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addDeleteValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDeleteSingleRow = useCallback(
|
||||||
|
(field: string) => {
|
||||||
|
addDeleteRow(field);
|
||||||
|
setTableData((prevTableData) => {
|
||||||
|
const newTableData = prevTableData.filter(
|
||||||
|
(item) => item.field !== field,
|
||||||
|
);
|
||||||
|
// console.log('newTableData', newTableData, prevTableData);
|
||||||
|
return newTableData;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addDeleteRow],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSaveManage = useCallback(
|
||||||
|
async (callback: () => void) => {
|
||||||
|
const { data: res } = await updateMetaData({
|
||||||
|
kb_id: id as string,
|
||||||
|
data: operations,
|
||||||
|
});
|
||||||
|
if (res.code === 0) {
|
||||||
|
message.success(t('message.success'));
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[operations, id, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSaveUpdateSingle = useCallback(
|
||||||
|
async (callback: () => void) => {
|
||||||
|
const reqData = util.tableDataToMetaDataJSON(tableData);
|
||||||
|
if (otherData?.id) {
|
||||||
|
const ret = await setDocumentMeta({
|
||||||
|
documentId: otherData?.id,
|
||||||
|
meta: JSON.stringify(reqData),
|
||||||
|
});
|
||||||
|
if (ret === 0) {
|
||||||
|
// message.success(t('message.success'));
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[tableData, otherData, setDocumentMeta],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSaveSettings = useCallback(
|
||||||
|
async (callback: () => void) => {
|
||||||
|
const data = util.tableDataToMetaDataSettingJSON(tableData);
|
||||||
|
callback();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
[tableData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSave = useCallback(
|
||||||
|
async ({ callback }: { callback: () => void }) => {
|
||||||
|
switch (type) {
|
||||||
|
case MetadataType.UpdateSingle:
|
||||||
|
handleSaveUpdateSingle(callback);
|
||||||
|
break;
|
||||||
|
case MetadataType.Manage:
|
||||||
|
handleSaveManage(callback);
|
||||||
|
break;
|
||||||
|
case MetadataType.Setting:
|
||||||
|
return handleSaveSettings(callback);
|
||||||
|
default:
|
||||||
|
handleSaveManage(callback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleSaveManage, type, handleSaveUpdateSingle, handleSaveSettings],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableData,
|
||||||
|
setTableData,
|
||||||
|
handleDeleteSingleValue,
|
||||||
|
handleDeleteSingleRow,
|
||||||
|
loading,
|
||||||
|
handleSave,
|
||||||
|
addUpdateValue,
|
||||||
|
addDeleteValue,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useManageMetadata = () => {
|
||||||
|
const [tableData, setTableData] = useState<IMetaDataTableData[]>([]);
|
||||||
|
const [config, setConfig] = useState<ShowManageMetadataModalProps>(
|
||||||
|
{} as ShowManageMetadataModalProps,
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
visible: manageMetadataVisible,
|
||||||
|
showModal,
|
||||||
|
hideModal: hideManageMetadataModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const showManageMetadataModal = useCallback(
|
||||||
|
(config?: ShowManageMetadataModalProps) => {
|
||||||
|
const { metadata } = config || {};
|
||||||
|
if (metadata) {
|
||||||
|
// const dataTemp = Object.entries(metadata).map(([key, value]) => {
|
||||||
|
// return {
|
||||||
|
// field: key,
|
||||||
|
// description: '',
|
||||||
|
// values: Array.isArray(value) ? value : [value],
|
||||||
|
// } as IMetaDataTableData;
|
||||||
|
// });
|
||||||
|
setTableData(metadata);
|
||||||
|
console.log('metadata-2', metadata);
|
||||||
|
}
|
||||||
|
console.log('metadata-3', metadata);
|
||||||
|
if (config) {
|
||||||
|
setConfig(config);
|
||||||
|
}
|
||||||
|
showModal();
|
||||||
|
},
|
||||||
|
[showModal],
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
manageMetadataVisible,
|
||||||
|
showManageMetadataModal,
|
||||||
|
hideManageMetadataModal,
|
||||||
|
tableData,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useManageValues = () => {
|
||||||
|
const [updateValues, setUpdateValues] = useState<{
|
||||||
|
field: string;
|
||||||
|
values: string[];
|
||||||
|
} | null>(null);
|
||||||
|
return { updateValues, setUpdateValues };
|
||||||
|
};
|
||||||
80
web/src/pages/dataset/components/metedata/interface.ts
Normal file
80
web/src/pages/dataset/components/metedata/interface.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { MetadataType } from './hook';
|
||||||
|
export type IMetaDataReturnType = Record<string, Array<Array<string | number>>>;
|
||||||
|
export type IMetaDataReturnJSONType = Record<
|
||||||
|
string,
|
||||||
|
Array<string | number> | string
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface IMetaDataReturnJSONSettingItem {
|
||||||
|
key: string;
|
||||||
|
description?: string;
|
||||||
|
enum?: string[];
|
||||||
|
}
|
||||||
|
export type IMetaDataReturnJSONSettings = Array<IMetaDataReturnJSONSettingItem>;
|
||||||
|
|
||||||
|
export type IMetaDataTableData = {
|
||||||
|
field: string;
|
||||||
|
description: string;
|
||||||
|
restrictDefinedValues?: boolean;
|
||||||
|
values: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IManageModalProps = {
|
||||||
|
title: ReactNode;
|
||||||
|
isShowDescription?: boolean;
|
||||||
|
isDeleteSingleValue?: boolean;
|
||||||
|
visible: boolean;
|
||||||
|
hideModal: () => void;
|
||||||
|
tableData?: IMetaDataTableData[];
|
||||||
|
isCanAdd: boolean;
|
||||||
|
type: MetadataType;
|
||||||
|
otherData?: Record<string, any>;
|
||||||
|
isEditField?: boolean;
|
||||||
|
isAddValue?: boolean;
|
||||||
|
isShowValueSwitch?: boolean;
|
||||||
|
isVerticalShowValue?: boolean;
|
||||||
|
success?: (data: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IManageValuesProps {
|
||||||
|
title: ReactNode;
|
||||||
|
visible: boolean;
|
||||||
|
isEditField?: boolean;
|
||||||
|
isAddValue?: boolean;
|
||||||
|
isShowDescription?: boolean;
|
||||||
|
isShowValueSwitch?: boolean;
|
||||||
|
isVerticalShowValue?: boolean;
|
||||||
|
data: IMetaDataTableData;
|
||||||
|
hideModal: () => void;
|
||||||
|
onSave: (data: IMetaDataTableData) => void;
|
||||||
|
addUpdateValue: (key: string, value: string | string[]) => void;
|
||||||
|
addDeleteValue: (key: string, value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteOperation {
|
||||||
|
key: string;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateOperation {
|
||||||
|
key: string;
|
||||||
|
value: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MetadataOperations {
|
||||||
|
deletes: DeleteOperation[];
|
||||||
|
updates: UpdateOperation[];
|
||||||
|
}
|
||||||
|
export interface ShowManageMetadataModalOptions {
|
||||||
|
title?: ReactNode | string;
|
||||||
|
}
|
||||||
|
export type ShowManageMetadataModalProps = Partial<IManageModalProps> & {
|
||||||
|
metadata?: IMetaDataTableData[];
|
||||||
|
isCanAdd: boolean;
|
||||||
|
type: MetadataType;
|
||||||
|
record?: Record<string, any>;
|
||||||
|
options?: ShowManageMetadataModalOptions;
|
||||||
|
title?: ReactNode | string;
|
||||||
|
isDeleteSingleValue?: boolean;
|
||||||
|
};
|
||||||
373
web/src/pages/dataset/components/metedata/manage-modal.tsx
Normal file
373
web/src/pages/dataset/components/metedata/manage-modal.tsx
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
import {
|
||||||
|
ConfirmDeleteDialog,
|
||||||
|
ConfirmDeleteDialogNode,
|
||||||
|
} from '@/components/confirm-delete-dialog';
|
||||||
|
import { EmptyType } from '@/components/empty/constant';
|
||||||
|
import Empty from '@/components/empty/empty';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table';
|
||||||
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
import { Plus, Settings, Trash2 } from 'lucide-react';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useManageMetaDataModal } from './hook';
|
||||||
|
import { IManageModalProps, IMetaDataTableData } from './interface';
|
||||||
|
import { ManageValuesModal } from './manage-values-modal';
|
||||||
|
export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
visible,
|
||||||
|
hideModal,
|
||||||
|
isDeleteSingleValue,
|
||||||
|
tableData: originalTableData,
|
||||||
|
isCanAdd,
|
||||||
|
type: metadataType,
|
||||||
|
otherData,
|
||||||
|
isEditField,
|
||||||
|
isAddValue,
|
||||||
|
isShowDescription = false,
|
||||||
|
isShowValueSwitch = false,
|
||||||
|
isVerticalShowValue = true,
|
||||||
|
success,
|
||||||
|
} = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [valueData, setValueData] = useState<IMetaDataTableData>({
|
||||||
|
field: '',
|
||||||
|
description: '',
|
||||||
|
values: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const [currentValueIndex, setCurrentValueIndex] = useState<number>(0);
|
||||||
|
const [deleteDialogContent, setDeleteDialogContent] = useState({
|
||||||
|
visible: false,
|
||||||
|
title: '',
|
||||||
|
name: '',
|
||||||
|
warnText: '',
|
||||||
|
onOk: () => {},
|
||||||
|
onCancel: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
tableData,
|
||||||
|
setTableData,
|
||||||
|
handleDeleteSingleValue,
|
||||||
|
handleDeleteSingleRow,
|
||||||
|
handleSave,
|
||||||
|
addUpdateValue,
|
||||||
|
addDeleteValue,
|
||||||
|
} = useManageMetaDataModal(originalTableData, metadataType, otherData);
|
||||||
|
|
||||||
|
const {
|
||||||
|
visible: manageValuesVisible,
|
||||||
|
showModal: showManageValuesModal,
|
||||||
|
hideModal: hideManageValuesModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const hideDeleteModal = () => {
|
||||||
|
setDeleteDialogContent({
|
||||||
|
visible: false,
|
||||||
|
title: '',
|
||||||
|
name: '',
|
||||||
|
warnText: '',
|
||||||
|
onOk: () => {},
|
||||||
|
onCancel: () => {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handAddValueRow = () => {
|
||||||
|
setValueData({
|
||||||
|
field: '',
|
||||||
|
description: '',
|
||||||
|
values: [],
|
||||||
|
});
|
||||||
|
setCurrentValueIndex(tableData.length || 0);
|
||||||
|
showManageValuesModal();
|
||||||
|
};
|
||||||
|
const handleEditValueRow = useCallback(
|
||||||
|
(data: IMetaDataTableData, index: number) => {
|
||||||
|
setCurrentValueIndex(index);
|
||||||
|
setValueData(data);
|
||||||
|
showManageValuesModal();
|
||||||
|
},
|
||||||
|
[showManageValuesModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns: ColumnDef<IMetaDataTableData>[] = useMemo(() => {
|
||||||
|
const cols: ColumnDef<IMetaDataTableData>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: 'field',
|
||||||
|
header: () => <span>{t('knowledgeDetails.metadata.field')}</span>,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-sm text-accent-primary">
|
||||||
|
{row.getValue('field')}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'description',
|
||||||
|
header: () => <span>{t('knowledgeDetails.metadata.description')}</span>,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-sm truncate max-w-32">
|
||||||
|
{row.getValue('description')}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'values',
|
||||||
|
header: () => <span>{t('knowledgeDetails.metadata.values')}</span>,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const values = row.getValue('values') as Array<string>;
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{values.length > 0 &&
|
||||||
|
values
|
||||||
|
.filter((value: string, index: number) => index < 2)
|
||||||
|
?.map((value: string) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={value}
|
||||||
|
variant={'ghost'}
|
||||||
|
className="border border-border-button"
|
||||||
|
aria-label="Edit"
|
||||||
|
>
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
<div className="text-sm truncate max-w-24">
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
{isDeleteSingleValue && (
|
||||||
|
<Button
|
||||||
|
variant={'delete'}
|
||||||
|
className="p-0 bg-transparent"
|
||||||
|
onClick={() => {
|
||||||
|
handleDeleteSingleValue(
|
||||||
|
row.getValue('field'),
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{values.length > 2 && (
|
||||||
|
<div className="text-text-secondary self-end">...</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'action',
|
||||||
|
header: () => <span>{t('knowledgeDetails.metadata.action')}</span>,
|
||||||
|
meta: {
|
||||||
|
cellClassName: 'w-12',
|
||||||
|
},
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className=" flex opacity-0 group-hover:opacity-100 gap-2">
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className="bg-transparent px-1 py-0"
|
||||||
|
onClick={() => {
|
||||||
|
handleEditValueRow(row.original, row.index);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Settings />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={'delete'}
|
||||||
|
className="p-0 bg-transparent"
|
||||||
|
onClick={() => {
|
||||||
|
setDeleteDialogContent({
|
||||||
|
visible: true,
|
||||||
|
title:
|
||||||
|
t('common.delete') +
|
||||||
|
' ' +
|
||||||
|
t('knowledgeDetails.metadata.metadata'),
|
||||||
|
name: row.getValue('field'),
|
||||||
|
warnText: t('knowledgeDetails.metadata.deleteWarn'),
|
||||||
|
onOk: () => {
|
||||||
|
hideDeleteModal();
|
||||||
|
handleDeleteSingleRow(row.getValue('field'));
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
hideDeleteModal();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (!isShowDescription) {
|
||||||
|
cols.splice(1, 1);
|
||||||
|
}
|
||||||
|
return cols;
|
||||||
|
}, [
|
||||||
|
handleDeleteSingleRow,
|
||||||
|
t,
|
||||||
|
handleDeleteSingleValue,
|
||||||
|
isShowDescription,
|
||||||
|
isDeleteSingleValue,
|
||||||
|
handleEditValueRow,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: tableData as IMetaDataTableData[],
|
||||||
|
columns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
manualPagination: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSaveValues = (data: IMetaDataTableData) => {
|
||||||
|
setTableData((prev) => {
|
||||||
|
if (currentValueIndex >= prev.length) {
|
||||||
|
return [...prev, data];
|
||||||
|
} else {
|
||||||
|
return prev.map((item, index) => {
|
||||||
|
if (index === currentValueIndex) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title={title}
|
||||||
|
open={visible}
|
||||||
|
onCancel={hideModal}
|
||||||
|
maskClosable={false}
|
||||||
|
okText={t('common.save')}
|
||||||
|
onOk={async () => {
|
||||||
|
const res = await handleSave({ callback: hideModal });
|
||||||
|
console.log('data', res);
|
||||||
|
success?.(res);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>{t('knowledgeDetails.metadata.metadata')}</div>
|
||||||
|
{isCanAdd && (
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className="border border-border-button"
|
||||||
|
onClick={handAddValueRow}
|
||||||
|
>
|
||||||
|
<Plus />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Table rootClassName="max-h-[800px]">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext(),
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody className="relative">
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
|
className="group"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext(),
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
<Empty type={EmptyType.Data} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
{manageValuesVisible && (
|
||||||
|
<ManageValuesModal
|
||||||
|
title={<div>{t('knowledgeDetails.metadata.editMetadata')}</div>}
|
||||||
|
visible={manageValuesVisible}
|
||||||
|
hideModal={hideManageValuesModal}
|
||||||
|
data={valueData}
|
||||||
|
onSave={handleSaveValues}
|
||||||
|
addUpdateValue={addUpdateValue}
|
||||||
|
addDeleteValue={addDeleteValue}
|
||||||
|
isEditField={isEditField || isCanAdd}
|
||||||
|
isAddValue={isAddValue || isCanAdd}
|
||||||
|
isShowDescription={isShowDescription}
|
||||||
|
isShowValueSwitch={isShowValueSwitch}
|
||||||
|
isVerticalShowValue={isVerticalShowValue}
|
||||||
|
// handleDeleteSingleValue={handleDeleteSingleValue}
|
||||||
|
// handleDeleteSingleRow={handleDeleteSingleRow}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{deleteDialogContent.visible && (
|
||||||
|
<ConfirmDeleteDialog
|
||||||
|
open={deleteDialogContent.visible}
|
||||||
|
onCancel={deleteDialogContent.onCancel}
|
||||||
|
onOk={deleteDialogContent.onOk}
|
||||||
|
title={deleteDialogContent.title}
|
||||||
|
content={{
|
||||||
|
node: (
|
||||||
|
<ConfirmDeleteDialogNode
|
||||||
|
name={deleteDialogContent.name}
|
||||||
|
warnText={deleteDialogContent.warnText}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,250 @@
|
|||||||
|
import EditTag from '@/components/edit-tag';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
|
import { Plus, Trash2 } from 'lucide-react';
|
||||||
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { IManageValuesProps, IMetaDataTableData } from './interface';
|
||||||
|
|
||||||
|
// Create a separate input component, wrapped with memo to avoid unnecessary re-renders
|
||||||
|
const ValueInputItem = memo(
|
||||||
|
({
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
onValueChange,
|
||||||
|
onDelete,
|
||||||
|
onBlur,
|
||||||
|
}: {
|
||||||
|
item: string;
|
||||||
|
index: number;
|
||||||
|
onValueChange: (index: number, value: string) => void;
|
||||||
|
onDelete: (index: number) => void;
|
||||||
|
onBlur: (index: number) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`value-item-${index}`}
|
||||||
|
className="flex items-center gap-2.5 w-full"
|
||||||
|
>
|
||||||
|
<div className="flex-1 w-full">
|
||||||
|
<Input
|
||||||
|
value={item}
|
||||||
|
onChange={(e) => onValueChange(index, e.target.value)}
|
||||||
|
onBlur={() => onBlur(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="delete"
|
||||||
|
className="border border-border-button px-1 h-6 w-6 rounded-sm"
|
||||||
|
onClick={() => onDelete(index)}
|
||||||
|
>
|
||||||
|
<Trash2 size={14} className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ManageValuesModal = (props: IManageValuesProps) => {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
data,
|
||||||
|
isEditField,
|
||||||
|
visible,
|
||||||
|
isAddValue,
|
||||||
|
isShowDescription,
|
||||||
|
isShowValueSwitch,
|
||||||
|
isVerticalShowValue,
|
||||||
|
hideModal,
|
||||||
|
onSave,
|
||||||
|
addUpdateValue,
|
||||||
|
addDeleteValue,
|
||||||
|
} = props;
|
||||||
|
const [metaData, setMetaData] = useState(data);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Use functional update to avoid closure issues
|
||||||
|
const handleChange = useCallback((field: string, value: any) => {
|
||||||
|
setMetaData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[field]: value,
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Maintain separate state for each input box
|
||||||
|
const [tempValues, setTempValues] = useState<string[]>([...data.values]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTempValues([...data.values]);
|
||||||
|
setMetaData(data);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const handleHideModal = useCallback(() => {
|
||||||
|
hideModal();
|
||||||
|
setMetaData({} as IMetaDataTableData);
|
||||||
|
}, [hideModal]);
|
||||||
|
|
||||||
|
const handleSave = useCallback(() => {
|
||||||
|
if (!metaData.restrictDefinedValues && isShowValueSwitch) {
|
||||||
|
const newMetaData = { ...metaData, values: [] };
|
||||||
|
onSave(newMetaData);
|
||||||
|
} else {
|
||||||
|
onSave(metaData);
|
||||||
|
}
|
||||||
|
handleHideModal();
|
||||||
|
}, [metaData, onSave, handleHideModal, isShowValueSwitch]);
|
||||||
|
|
||||||
|
// Handle value changes, only update temporary state
|
||||||
|
const handleValueChange = useCallback((index: number, value: string) => {
|
||||||
|
setTempValues((prev) => {
|
||||||
|
const newValues = [...prev];
|
||||||
|
newValues[index] = value;
|
||||||
|
|
||||||
|
return newValues;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Handle blur event, synchronize to main state
|
||||||
|
const handleValueBlur = useCallback(() => {
|
||||||
|
addUpdateValue(metaData.field, [...tempValues]);
|
||||||
|
handleChange('values', [...tempValues]);
|
||||||
|
}, [handleChange, tempValues, metaData, addUpdateValue]);
|
||||||
|
|
||||||
|
// Handle delete operation
|
||||||
|
const handleDelete = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
setTempValues((prev) => {
|
||||||
|
const newTempValues = [...prev];
|
||||||
|
addDeleteValue(metaData.field, newTempValues[index]);
|
||||||
|
newTempValues.splice(index, 1);
|
||||||
|
return newTempValues;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Synchronize to main state
|
||||||
|
setMetaData((prev) => {
|
||||||
|
const newMetaDataValues = [...prev.values];
|
||||||
|
newMetaDataValues.splice(index, 1);
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
values: newMetaDataValues,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[addDeleteValue, metaData],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle adding new value
|
||||||
|
const handleAddValue = useCallback(() => {
|
||||||
|
setTempValues((prev) => [...prev, '']);
|
||||||
|
|
||||||
|
// Synchronize to main state
|
||||||
|
setMetaData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
values: [...prev.values, ''],
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={title}
|
||||||
|
open={visible}
|
||||||
|
onCancel={handleHideModal}
|
||||||
|
className="!w-[460px]"
|
||||||
|
okText={t('common.save')}
|
||||||
|
onOk={handleSave}
|
||||||
|
maskClosable={false}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{!isEditField && (
|
||||||
|
<div className="text-base p-5 border border-border-button rounded-lg">
|
||||||
|
{metaData.field}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isEditField && (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div>{t('knowledgeDetails.metadata.fieldName')}</div>
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
value={metaData.field}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleChange('field', e.target?.value || '');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isShowDescription && (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div>{t('knowledgeDetails.metadata.description')}</div>
|
||||||
|
<div>
|
||||||
|
<Textarea
|
||||||
|
value={metaData.description}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleChange('description', e.target?.value || '');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isShowValueSwitch && (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div>{t('knowledgeDetails.metadata.restrictDefinedValues')}</div>
|
||||||
|
<div>
|
||||||
|
<Switch
|
||||||
|
checked={metaData.restrictDefinedValues || false}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
handleChange('restrictDefinedValues', checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{metaData.restrictDefinedValues && (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>{t('knowledgeDetails.metadata.values')}</div>
|
||||||
|
{isAddValue && isVerticalShowValue && (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className="border border-border-button"
|
||||||
|
onClick={handleAddValue}
|
||||||
|
>
|
||||||
|
<Plus />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{isVerticalShowValue && (
|
||||||
|
<div className="flex flex-col gap-2 w-full">
|
||||||
|
{tempValues?.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<ValueInputItem
|
||||||
|
key={`value-item-${index}`}
|
||||||
|
item={item}
|
||||||
|
index={index}
|
||||||
|
onValueChange={handleValueChange}
|
||||||
|
onDelete={handleDelete}
|
||||||
|
onBlur={handleValueBlur}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isVerticalShowValue && (
|
||||||
|
<EditTag
|
||||||
|
value={metaData.values}
|
||||||
|
onChange={(value) => handleChange('values', value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,5 +1,11 @@
|
|||||||
|
import {
|
||||||
|
FormFieldConfig,
|
||||||
|
FormFieldType,
|
||||||
|
RenderField,
|
||||||
|
} from '@/components/dynamic-form';
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
@ -13,8 +19,19 @@ import { Switch } from '@/components/ui/switch';
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
|
import { Settings } from 'lucide-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { FieldValues, useFormContext } from 'react-hook-form';
|
import {
|
||||||
|
ControllerRenderProps,
|
||||||
|
FieldValues,
|
||||||
|
useFormContext,
|
||||||
|
} from 'react-hook-form';
|
||||||
|
import {
|
||||||
|
MetadataType,
|
||||||
|
useManageMetadata,
|
||||||
|
util,
|
||||||
|
} from '../../components/metedata/hook';
|
||||||
|
import { ManageMetadataModal } from '../../components/metedata/manage-modal';
|
||||||
import {
|
import {
|
||||||
useHandleKbEmbedding,
|
useHandleKbEmbedding,
|
||||||
useHasParsedDocument,
|
useHasParsedDocument,
|
||||||
@ -304,3 +321,84 @@ export function OverlappedPercent() {
|
|||||||
></SliderInputFormField>
|
></SliderInputFormField>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AutoMetadata() {
|
||||||
|
// get metadata field
|
||||||
|
const form = useFormContext();
|
||||||
|
const {
|
||||||
|
manageMetadataVisible,
|
||||||
|
showManageMetadataModal,
|
||||||
|
hideManageMetadataModal,
|
||||||
|
tableData,
|
||||||
|
config: metadataConfig,
|
||||||
|
} = useManageMetadata();
|
||||||
|
const autoMetadataField: FormFieldConfig = {
|
||||||
|
name: 'parser_config.enable_metadata',
|
||||||
|
label: t('knowledgeConfiguration.autoMetadata'),
|
||||||
|
type: FormFieldType.Custom,
|
||||||
|
horizontal: true,
|
||||||
|
defaultValue: true,
|
||||||
|
|
||||||
|
render: (fieldProps: ControllerRenderProps) => (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
const metadata = form.getValues('parser_config.metadata');
|
||||||
|
const tableMetaData =
|
||||||
|
util.metaDataSettingJSONToMetaDataTableData(metadata);
|
||||||
|
showManageMetadataModal({
|
||||||
|
metadata: tableMetaData,
|
||||||
|
isCanAdd: true,
|
||||||
|
type: MetadataType.Setting,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Settings />
|
||||||
|
{t('knowledgeConfiguration.settings')}
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<Switch
|
||||||
|
checked={fieldProps.value}
|
||||||
|
onCheckedChange={fieldProps.onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RenderField field={autoMetadataField} />
|
||||||
|
{manageMetadataVisible && (
|
||||||
|
<ManageMetadataModal
|
||||||
|
title={
|
||||||
|
metadataConfig.title || (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="text-base font-normal">
|
||||||
|
{t('knowledgeDetails.metadata.metadataGenerationSettings')}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-text-secondary">
|
||||||
|
{t('knowledgeDetails.metadata.changesAffectNewParses')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
visible={manageMetadataVisible}
|
||||||
|
hideModal={hideManageMetadataModal}
|
||||||
|
// selectedRowKeys={selectedRowKeys}
|
||||||
|
tableData={tableData}
|
||||||
|
isCanAdd={metadataConfig.isCanAdd}
|
||||||
|
isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
|
||||||
|
type={metadataConfig.type}
|
||||||
|
otherData={metadataConfig.record}
|
||||||
|
isShowDescription={true}
|
||||||
|
isShowValueSwitch={true}
|
||||||
|
isVerticalShowValue={false}
|
||||||
|
success={(data) => {
|
||||||
|
form.setValue('parser_config.metadata', data || []);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,11 @@ import {
|
|||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
} from '../configuration-form-container';
|
} from '../configuration-form-container';
|
||||||
import { EnableTocToggle, OverlappedPercent } from './common-item';
|
import {
|
||||||
|
AutoMetadata,
|
||||||
|
EnableTocToggle,
|
||||||
|
OverlappedPercent,
|
||||||
|
} from './common-item';
|
||||||
|
|
||||||
export function NaiveConfiguration() {
|
export function NaiveConfiguration() {
|
||||||
return (
|
return (
|
||||||
@ -22,6 +26,7 @@ export function NaiveConfiguration() {
|
|||||||
<DelimiterFormField></DelimiterFormField>
|
<DelimiterFormField></DelimiterFormField>
|
||||||
<ChildrenDelimiterForm />
|
<ChildrenDelimiterForm />
|
||||||
<EnableTocToggle />
|
<EnableTocToggle />
|
||||||
|
<AutoMetadata />
|
||||||
<OverlappedPercent />
|
<OverlappedPercent />
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
<ConfigurationFormContainer>
|
<ConfigurationFormContainer>
|
||||||
|
|||||||
@ -83,6 +83,18 @@ export const formSchema = z
|
|||||||
path: ['entity_types'],
|
path: ['entity_types'],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
metadata: z
|
||||||
|
.array(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
key: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
enum: z.array(z.string().optional()).optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
enable_metadata: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
pagerank: z.number(),
|
pagerank: z.number(),
|
||||||
|
|||||||
@ -90,6 +90,8 @@ export default function DatasetSettings() {
|
|||||||
entity_types: initialEntityTypes,
|
entity_types: initialEntityTypes,
|
||||||
method: MethodValue.Light,
|
method: MethodValue.Light,
|
||||||
},
|
},
|
||||||
|
metadata: [],
|
||||||
|
enable_metadata: false,
|
||||||
},
|
},
|
||||||
pipeline_id: '',
|
pipeline_id: '',
|
||||||
parseType: 1,
|
parseType: 1,
|
||||||
@ -238,11 +240,8 @@ export default function DatasetSettings() {
|
|||||||
}
|
}
|
||||||
return connector;
|
return connector;
|
||||||
});
|
});
|
||||||
console.log('🚀 ~ DatasetSettings ~ connectors:', connectors);
|
|
||||||
setSourceData(connectors as IDataSourceNodeProps[]);
|
setSourceData(connectors as IDataSourceNodeProps[]);
|
||||||
form.setValue('connectors', connectors || []);
|
form.setValue('connectors', connectors || []);
|
||||||
// form.setValue('pipeline_name', data.name || '');
|
|
||||||
// form.setValue('pipeline_avatar', data.avatar || '');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,11 @@ export function SavingButton() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
let beValid = await form.formControl.trigger();
|
let beValid = await form.trigger();
|
||||||
|
if (!beValid) {
|
||||||
|
const errors = form.formState.errors;
|
||||||
|
console.error('Validation errors:', errors);
|
||||||
|
}
|
||||||
if (beValid) {
|
if (beValid) {
|
||||||
form.handleSubmit(async (values) => {
|
form.handleSubmit(async (values) => {
|
||||||
console.log('saveKnowledgeConfiguration: ', values);
|
console.log('saveKnowledgeConfiguration: ', values);
|
||||||
|
|||||||
@ -32,19 +32,20 @@ import { getExtension } from '@/utils/document-util';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { ShowManageMetadataModalProps } from '../components/metedata/interface';
|
||||||
import ProcessLogModal from '../process-log-modal';
|
import ProcessLogModal from '../process-log-modal';
|
||||||
import { useShowLog } from './hooks';
|
import { useShowLog } from './hooks';
|
||||||
import { SetMetaDialog } from './set-meta-dialog';
|
|
||||||
import { useChangeDocumentParser } from './use-change-document-parser';
|
import { useChangeDocumentParser } from './use-change-document-parser';
|
||||||
import { useDatasetTableColumns } from './use-dataset-table-columns';
|
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';
|
|
||||||
|
|
||||||
export type DatasetTableProps = Pick<
|
export type DatasetTableProps = Pick<
|
||||||
ReturnType<typeof useFetchDocumentList>,
|
ReturnType<typeof useFetchDocumentList>,
|
||||||
'documents' | 'setPagination' | 'pagination' | 'loading'
|
'documents' | 'setPagination' | 'pagination' | 'loading'
|
||||||
> &
|
> &
|
||||||
Pick<UseRowSelectionType, 'rowSelection' | 'setRowSelection'>;
|
Pick<UseRowSelectionType, 'rowSelection' | 'setRowSelection'> & {
|
||||||
|
showManageMetadataModal: (config: ShowManageMetadataModalProps) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export function DatasetTable({
|
export function DatasetTable({
|
||||||
documents,
|
documents,
|
||||||
@ -52,6 +53,7 @@ export function DatasetTable({
|
|||||||
setPagination,
|
setPagination,
|
||||||
rowSelection,
|
rowSelection,
|
||||||
setRowSelection,
|
setRowSelection,
|
||||||
|
showManageMetadataModal,
|
||||||
}: DatasetTableProps) {
|
}: 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>(
|
||||||
@ -78,20 +80,19 @@ export function DatasetTable({
|
|||||||
initialName,
|
initialName,
|
||||||
} = useRenameDocument();
|
} = useRenameDocument();
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
showSetMetaModal,
|
// hideSetMetaModal,
|
||||||
hideSetMetaModal,
|
// setMetaVisible,
|
||||||
setMetaVisible,
|
// setMetaLoading,
|
||||||
setMetaLoading,
|
// onSetMetaModalOk,
|
||||||
onSetMetaModalOk,
|
// metaRecord,
|
||||||
metaRecord,
|
// } = useSaveMeta();
|
||||||
} = useSaveMeta();
|
|
||||||
const { showLog, logInfo, logVisible, hideLog } = useShowLog(documents);
|
const { showLog, logInfo, logVisible, hideLog } = useShowLog(documents);
|
||||||
|
|
||||||
const columns = useDatasetTableColumns({
|
const columns = useDatasetTableColumns({
|
||||||
showChangeParserModal,
|
showChangeParserModal,
|
||||||
showRenameModal,
|
showRenameModal,
|
||||||
showSetMetaModal,
|
showManageMetadataModal,
|
||||||
showLog,
|
showLog,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -207,14 +208,14 @@ export function DatasetTable({
|
|||||||
></RenameDialog>
|
></RenameDialog>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{setMetaVisible && (
|
{/* {setMetaVisible && (
|
||||||
<SetMetaDialog
|
<SetMetaDialog
|
||||||
hideModal={hideSetMetaModal}
|
hideModal={hideSetMetaModal}
|
||||||
loading={setMetaLoading}
|
loading={setMetaLoading}
|
||||||
onOk={onSetMetaModalOk}
|
onOk={onSetMetaModalOk}
|
||||||
initialMetaData={metaRecord.meta_fields}
|
initialMetaData={metaRecord.meta_fields}
|
||||||
></SetMetaDialog>
|
></SetMetaDialog>
|
||||||
)}
|
)} */}
|
||||||
{logVisible && (
|
{logVisible && (
|
||||||
<ProcessLogModal
|
<ProcessLogModal
|
||||||
title={t('knowledgeDetails.fileLogs')}
|
title={t('knowledgeDetails.fileLogs')}
|
||||||
|
|||||||
@ -13,9 +13,11 @@ import {
|
|||||||
import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection';
|
import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection';
|
||||||
import { useFetchDocumentList } from '@/hooks/use-document-request';
|
import { useFetchDocumentList } from '@/hooks/use-document-request';
|
||||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||||
import { Upload } from 'lucide-react';
|
import { Pen, Upload } from 'lucide-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useManageMetadata } from '../components/metedata/hook';
|
||||||
|
import { ManageMetadataModal } from '../components/metedata/manage-modal';
|
||||||
import { DatasetTable } from './dataset-table';
|
import { DatasetTable } from './dataset-table';
|
||||||
import Generate from './generate-button/generate';
|
import Generate from './generate-button/generate';
|
||||||
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
|
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
|
||||||
@ -61,6 +63,14 @@ export default function Dataset() {
|
|||||||
showCreateModal,
|
showCreateModal,
|
||||||
} = useCreateEmptyDocument();
|
} = useCreateEmptyDocument();
|
||||||
|
|
||||||
|
const {
|
||||||
|
manageMetadataVisible,
|
||||||
|
showManageMetadataModal,
|
||||||
|
hideManageMetadataModal,
|
||||||
|
tableData,
|
||||||
|
config: metadataConfig,
|
||||||
|
} = useManageMetadata();
|
||||||
|
|
||||||
const { rowSelection, rowSelectionIsEmpty, setRowSelection, selectedCount } =
|
const { rowSelection, rowSelectionIsEmpty, setRowSelection, selectedCount } =
|
||||||
useRowSelection();
|
useRowSelection();
|
||||||
|
|
||||||
@ -91,6 +101,18 @@ export default function Dataset() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
preChildren={
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className="border border-border-button"
|
||||||
|
onClick={() => showManageMetadataModal()}
|
||||||
|
>
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
<Pen size={14} />
|
||||||
|
{t('knowledgeDetails.metadata.metadata')}
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@ -119,6 +141,7 @@ export default function Dataset() {
|
|||||||
setPagination={setPagination}
|
setPagination={setPagination}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
setRowSelection={setRowSelection}
|
setRowSelection={setRowSelection}
|
||||||
|
showManageMetadataModal={showManageMetadataModal}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
></DatasetTable>
|
></DatasetTable>
|
||||||
{documentUploadVisible && (
|
{documentUploadVisible && (
|
||||||
@ -137,6 +160,30 @@ export default function Dataset() {
|
|||||||
title={'File Name'}
|
title={'File Name'}
|
||||||
></RenameDialog>
|
></RenameDialog>
|
||||||
)}
|
)}
|
||||||
|
{manageMetadataVisible && (
|
||||||
|
<ManageMetadataModal
|
||||||
|
title={
|
||||||
|
metadataConfig.title || (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="text-base font-normal">
|
||||||
|
{t('knowledgeDetails.metadata.manageMetadata')}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-text-secondary">
|
||||||
|
{t('knowledgeDetails.metadata.manageMetadataForDataset')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
visible={manageMetadataVisible}
|
||||||
|
hideModal={hideManageMetadataModal}
|
||||||
|
// selectedRowKeys={selectedRowKeys}
|
||||||
|
tableData={tableData}
|
||||||
|
isCanAdd={metadataConfig.isCanAdd}
|
||||||
|
isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
|
||||||
|
type={metadataConfig.type}
|
||||||
|
otherData={metadataConfig.record}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,20 +16,23 @@ import { formatDate } from '@/utils/date';
|
|||||||
import { ColumnDef } from '@tanstack/table-core';
|
import { ColumnDef } from '@tanstack/table-core';
|
||||||
import { ArrowUpDown, MonitorUp } from 'lucide-react';
|
import { ArrowUpDown, MonitorUp } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { MetadataType, util } from '../components/metedata/hook';
|
||||||
|
import { ShowManageMetadataModalProps } from '../components/metedata/interface';
|
||||||
import { DatasetActionCell } from './dataset-action-cell';
|
import { DatasetActionCell } from './dataset-action-cell';
|
||||||
import { ParsingStatusCell } from './parsing-status-cell';
|
import { ParsingStatusCell } from './parsing-status-cell';
|
||||||
import { UseChangeDocumentParserShowType } from './use-change-document-parser';
|
import { UseChangeDocumentParserShowType } from './use-change-document-parser';
|
||||||
import { UseRenameDocumentShowType } from './use-rename-document';
|
import { UseRenameDocumentShowType } from './use-rename-document';
|
||||||
import { UseSaveMetaShowType } from './use-save-meta';
|
|
||||||
|
|
||||||
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
|
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
|
||||||
UseRenameDocumentShowType &
|
UseRenameDocumentShowType & {
|
||||||
UseSaveMetaShowType & { showLog: (record: IDocumentInfo) => void };
|
showLog: (record: IDocumentInfo) => void;
|
||||||
|
showManageMetadataModal: (config: ShowManageMetadataModalProps) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export function useDatasetTableColumns({
|
export function useDatasetTableColumns({
|
||||||
showChangeParserModal,
|
showChangeParserModal,
|
||||||
showRenameModal,
|
showRenameModal,
|
||||||
showSetMetaModal,
|
showManageMetadataModal,
|
||||||
showLog,
|
showLog,
|
||||||
}: UseDatasetTableColumnsType) {
|
}: UseDatasetTableColumnsType) {
|
||||||
const { t } = useTranslation('translation', {
|
const { t } = useTranslation('translation', {
|
||||||
@ -174,7 +177,26 @@ export function useDatasetTableColumns({
|
|||||||
<ParsingStatusCell
|
<ParsingStatusCell
|
||||||
record={row.original}
|
record={row.original}
|
||||||
showChangeParserModal={showChangeParserModal}
|
showChangeParserModal={showChangeParserModal}
|
||||||
showSetMetaModal={showSetMetaModal}
|
showSetMetaModal={(row) =>
|
||||||
|
showManageMetadataModal({
|
||||||
|
metadata: util.JSONToMetaDataTableData(row.meta_fields || {}),
|
||||||
|
isCanAdd: true,
|
||||||
|
type: MetadataType.UpdateSingle,
|
||||||
|
record: row,
|
||||||
|
title: (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="text-base font-normal">
|
||||||
|
{t('metadata.editMetadata')}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-text-secondary">
|
||||||
|
{t('metadata.editMetadataForDataset')}
|
||||||
|
{row.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
isDeleteSingleValue: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
showLog={showLog}
|
showLog={showLog}
|
||||||
></ParsingStatusCell>
|
></ParsingStatusCell>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,18 +16,10 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/components/ui/select';
|
|
||||||
import { LanguageTranslationMap } from '@/constants/common';
|
|
||||||
import { FormLayout } from '@/constants/form';
|
import { FormLayout } from '@/constants/form';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@ -42,13 +34,6 @@ const FormId = 'dataset-creating-form';
|
|||||||
export function InputForm({ onOk }: IModalProps<any>) {
|
export function InputForm({ onOk }: IModalProps<any>) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const languageOptions = useMemo(() => {
|
|
||||||
return Object.keys(LanguageTranslationMap).map((x) => ({
|
|
||||||
label: x,
|
|
||||||
value: x,
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const FormSchema = z
|
const FormSchema = z
|
||||||
.object({
|
.object({
|
||||||
name: z
|
name: z
|
||||||
@ -66,7 +51,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
|||||||
.trim(),
|
.trim(),
|
||||||
parser_id: z.string().optional(),
|
parser_id: z.string().optional(),
|
||||||
pipeline_id: z.string().optional(),
|
pipeline_id: z.string().optional(),
|
||||||
language: z.string().optional(),
|
|
||||||
})
|
})
|
||||||
.superRefine((data, ctx) => {
|
.superRefine((data, ctx) => {
|
||||||
// When parseType === 1, parser_id is required
|
// When parseType === 1, parser_id is required
|
||||||
@ -80,8 +64,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
|||||||
path: ['parser_id'],
|
path: ['parser_id'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('form-data', data);
|
|
||||||
// When parseType === 1, pipline_id required
|
// When parseType === 1, pipline_id required
|
||||||
if (data.parseType === 2 && !data.pipeline_id) {
|
if (data.parseType === 2 && !data.pipeline_id) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
@ -99,7 +81,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
|||||||
parseType: 1,
|
parseType: 1,
|
||||||
parser_id: '',
|
parser_id: '',
|
||||||
embd_id: '',
|
embd_id: '',
|
||||||
language: 'English',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -147,33 +128,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="language"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('common.language')}</FormLabel>
|
|
||||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t('common.languagePlaceholder')}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{languageOptions.map((option) => (
|
|
||||||
<SelectItem key={option.value} value={option.value}>
|
|
||||||
{option.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<EmbeddingModelItem line={2} isEdit={false} />
|
<EmbeddingModelItem line={2} isEdit={false} />
|
||||||
<ParseTypeItem />
|
<ParseTypeItem />
|
||||||
{parseType === 1 && <ChunkMethodItem></ChunkMethodItem>}
|
{parseType === 1 && <ChunkMethodItem></ChunkMethodItem>}
|
||||||
|
|||||||
@ -215,6 +215,10 @@ const methods = {
|
|||||||
url: check_embedding,
|
url: check_embedding,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
},
|
},
|
||||||
|
// getMetaData: {
|
||||||
|
// url: getMetaData,
|
||||||
|
// method: 'get',
|
||||||
|
// },
|
||||||
};
|
};
|
||||||
|
|
||||||
const kbService = registerServer<keyof typeof methods>(methods, request);
|
const kbService = registerServer<keyof typeof methods>(methods, request);
|
||||||
@ -251,6 +255,11 @@ export const listDocument = (
|
|||||||
export const documentFilter = (kb_id: string) =>
|
export const documentFilter = (kb_id: string) =>
|
||||||
request.post(api.get_dataset_filter, { kb_id });
|
request.post(api.get_dataset_filter, { kb_id });
|
||||||
|
|
||||||
|
export const getMetaDataService = ({ kb_id }: { kb_id: string }) =>
|
||||||
|
request.post(api.getMetaData, { data: { kb_id } });
|
||||||
|
export const updateMetaData = ({ kb_id, data }: { kb_id: string; data: any }) =>
|
||||||
|
request.post(api.updateMetaData, { data: { kb_id, data } });
|
||||||
|
|
||||||
export const listDataPipelineLogDocument = (
|
export const listDataPipelineLogDocument = (
|
||||||
params?: IFetchKnowledgeListRequestParams,
|
params?: IFetchKnowledgeListRequestParams,
|
||||||
body?: IFetchDocumentListRequestBody,
|
body?: IFetchDocumentListRequestBody,
|
||||||
|
|||||||
@ -77,6 +77,8 @@ export default {
|
|||||||
unbindPipelineTask: ({ kb_id, type }: { kb_id: string; type: string }) =>
|
unbindPipelineTask: ({ kb_id, type }: { kb_id: string; type: string }) =>
|
||||||
`${api_host}/kb/unbind_task?kb_id=${kb_id}&pipeline_task_type=${type}`,
|
`${api_host}/kb/unbind_task?kb_id=${kb_id}&pipeline_task_type=${type}`,
|
||||||
pipelineRerun: `${api_host}/canvas/rerun`,
|
pipelineRerun: `${api_host}/canvas/rerun`,
|
||||||
|
getMetaData: `${api_host}/document/metadata/summary`,
|
||||||
|
updateMetaData: `${api_host}/document/metadata/update`,
|
||||||
|
|
||||||
// tags
|
// tags
|
||||||
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,
|
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,
|
||||||
|
|||||||
Reference in New Issue
Block a user