mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-02 02:25:31 +08:00
Fix:Metadata saving, copywriting and other related issues (#12169)
### What problem does this PR solve? Fix:Bugs Fixed - Text overflow issues that caused rendering problems - Metadata saving, copywriting and other related issues ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -9,6 +9,7 @@ import kbService, {
|
||||
updateMetaData,
|
||||
} from '@/services/knowledge-service';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { TFunction } from 'i18next';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'umi';
|
||||
@ -19,12 +20,43 @@ import {
|
||||
IMetaDataTableData,
|
||||
MetadataOperations,
|
||||
ShowManageMetadataModalProps,
|
||||
} from './interface';
|
||||
} from '../interface';
|
||||
export enum MetadataType {
|
||||
Manage = 1,
|
||||
UpdateSingle = 2,
|
||||
Setting = 3,
|
||||
SingleFileSetting = 4,
|
||||
}
|
||||
|
||||
export const MetadataDeleteMap = (
|
||||
t: TFunction<'translation', undefined>,
|
||||
): Record<
|
||||
MetadataType,
|
||||
{ title: string; warnFieldText: string; warnValueText: string }
|
||||
> => {
|
||||
return {
|
||||
[MetadataType.Manage]: {
|
||||
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
|
||||
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldAllWarn'),
|
||||
warnValueText: t('knowledgeDetails.metadata.deleteManageValueAllWarn'),
|
||||
},
|
||||
[MetadataType.Setting]: {
|
||||
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
|
||||
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldAllWarn'),
|
||||
warnValueText: t('knowledgeDetails.metadata.deleteManageValueAllWarn'),
|
||||
},
|
||||
[MetadataType.UpdateSingle]: {
|
||||
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
|
||||
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldSingleWarn'),
|
||||
warnValueText: t('knowledgeDetails.metadata.deleteManageValueSingleWarn'),
|
||||
},
|
||||
[MetadataType.SingleFileSetting]: {
|
||||
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
|
||||
warnFieldText: t('knowledgeDetails.metadata.deleteManageFieldSingleWarn'),
|
||||
warnValueText: t('knowledgeDetails.metadata.deleteManageValueSingleWarn'),
|
||||
},
|
||||
};
|
||||
};
|
||||
export const util = {
|
||||
changeToMetaDataTableData(data: IMetaDataReturnType): IMetaDataTableData[] {
|
||||
return Object.entries(data).map(([key, value]) => {
|
||||
@ -42,10 +74,21 @@ export const util = {
|
||||
data: Record<string, string | string[]>,
|
||||
): IMetaDataTableData[] {
|
||||
return Object.entries(data).map(([key, value]) => {
|
||||
let thisValue = [] as string[];
|
||||
if (value && Array.isArray(value)) {
|
||||
thisValue = value;
|
||||
} else if (value && typeof value === 'string') {
|
||||
thisValue = [value];
|
||||
} else if (value && typeof value === 'object') {
|
||||
thisValue = [JSON.stringify(value)];
|
||||
} else if (value) {
|
||||
thisValue = [value.toString()];
|
||||
}
|
||||
|
||||
return {
|
||||
field: key,
|
||||
description: '',
|
||||
values: value,
|
||||
values: thisValue,
|
||||
} as IMetaDataTableData;
|
||||
});
|
||||
},
|
||||
@ -103,12 +146,42 @@ export const useMetadataOperations = () => {
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// const addUpdateValue = useCallback(
|
||||
// (key: string, value: string | string[]) => {
|
||||
// setOperations((prev) => ({
|
||||
// ...prev,
|
||||
// updates: [...prev.updates, { key, value }],
|
||||
// }));
|
||||
// },
|
||||
// [],
|
||||
// );
|
||||
const addUpdateValue = useCallback(
|
||||
(key: string, value: string | string[]) => {
|
||||
setOperations((prev) => ({
|
||||
...prev,
|
||||
updates: [...prev.updates, { key, value }],
|
||||
}));
|
||||
(key: string, originalValue: string, newValue: string) => {
|
||||
setOperations((prev) => {
|
||||
const existsIndex = prev.updates.findIndex(
|
||||
(update) => update.key === key && update.match === originalValue,
|
||||
);
|
||||
|
||||
if (existsIndex > -1) {
|
||||
const updatedUpdates = [...prev.updates];
|
||||
updatedUpdates[existsIndex] = {
|
||||
key,
|
||||
match: originalValue,
|
||||
value: newValue,
|
||||
};
|
||||
return {
|
||||
...prev,
|
||||
updates: updatedUpdates,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
updates: [
|
||||
...prev.updates,
|
||||
{ key, match: originalValue, value: newValue },
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
@ -195,8 +268,13 @@ export const useManageMetaDataModal = (
|
||||
|
||||
const [tableData, setTableData] = useState<IMetaDataTableData[]>(metaData);
|
||||
const queryClient = useQueryClient();
|
||||
const { operations, addDeleteRow, addDeleteValue, addUpdateValue } =
|
||||
useMetadataOperations();
|
||||
const {
|
||||
operations,
|
||||
addDeleteRow,
|
||||
addDeleteValue,
|
||||
addUpdateValue,
|
||||
resetOperations,
|
||||
} = useMetadataOperations();
|
||||
|
||||
const { setDocumentMeta } = useSetDocumentMeta();
|
||||
|
||||
@ -265,11 +343,12 @@ export const useManageMetaDataModal = (
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [DocumentApiAction.FetchDocumentList],
|
||||
});
|
||||
resetOperations();
|
||||
message.success(t('message.operated'));
|
||||
callback();
|
||||
}
|
||||
},
|
||||
[operations, id, t, queryClient],
|
||||
[operations, id, t, queryClient, resetOperations],
|
||||
);
|
||||
|
||||
const handleSaveUpdateSingle = useCallback(
|
||||
@ -303,7 +382,26 @@ export const useManageMetaDataModal = (
|
||||
|
||||
return data;
|
||||
},
|
||||
[tableData, id],
|
||||
[tableData, id, t],
|
||||
);
|
||||
|
||||
const handleSaveSingleFileSettings = useCallback(
|
||||
async (callback: () => void) => {
|
||||
const data = util.tableDataToMetaDataSettingJSON(tableData);
|
||||
if (otherData?.documentId) {
|
||||
const { data: res } = await kbService.documentUpdateMetaData({
|
||||
doc_id: otherData.documentId,
|
||||
metadata: data,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
message.success(t('message.operated'));
|
||||
callback?.();
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
[tableData, t, otherData],
|
||||
);
|
||||
|
||||
const handleSave = useCallback(
|
||||
@ -317,12 +415,20 @@ export const useManageMetaDataModal = (
|
||||
break;
|
||||
case MetadataType.Setting:
|
||||
return handleSaveSettings(callback);
|
||||
case MetadataType.SingleFileSetting:
|
||||
return handleSaveSingleFileSettings(callback);
|
||||
default:
|
||||
handleSaveManage(callback);
|
||||
break;
|
||||
}
|
||||
},
|
||||
[handleSaveManage, type, handleSaveUpdateSingle, handleSaveSettings],
|
||||
[
|
||||
handleSaveManage,
|
||||
type,
|
||||
handleSaveUpdateSingle,
|
||||
handleSaveSettings,
|
||||
handleSaveSingleFileSettings,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
@ -377,11 +483,3 @@ export const useManageMetadata = () => {
|
||||
config,
|
||||
};
|
||||
};
|
||||
|
||||
export const useManageValues = () => {
|
||||
const [updateValues, setUpdateValues] = useState<{
|
||||
field: string;
|
||||
values: string[];
|
||||
} | null>(null);
|
||||
return { updateValues, setUpdateValues };
|
||||
};
|
||||
@ -0,0 +1,208 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MetadataDeleteMap, MetadataType } from '../hooks/use-manage-modal';
|
||||
import { IManageValuesProps, IMetaDataTableData } from '../interface';
|
||||
|
||||
export const useManageValues = (props: IManageValuesProps) => {
|
||||
const {
|
||||
data,
|
||||
|
||||
isShowValueSwitch,
|
||||
hideModal,
|
||||
onSave,
|
||||
addUpdateValue,
|
||||
addDeleteValue,
|
||||
existsKeys,
|
||||
type,
|
||||
} = props;
|
||||
const { t } = useTranslation();
|
||||
const [metaData, setMetaData] = useState(data);
|
||||
const [valueError, setValueError] = useState<Record<string, string>>({
|
||||
field: '',
|
||||
values: '',
|
||||
});
|
||||
const [deleteDialogContent, setDeleteDialogContent] = useState({
|
||||
visible: false,
|
||||
title: '',
|
||||
name: '',
|
||||
warnText: '',
|
||||
onOk: () => {},
|
||||
onCancel: () => {},
|
||||
});
|
||||
const hideDeleteModal = () => {
|
||||
setDeleteDialogContent({
|
||||
visible: false,
|
||||
title: '',
|
||||
name: '',
|
||||
warnText: '',
|
||||
onOk: () => {},
|
||||
onCancel: () => {},
|
||||
});
|
||||
};
|
||||
|
||||
// Use functional update to avoid closure issues
|
||||
const handleChange = useCallback(
|
||||
(field: string, value: any) => {
|
||||
if (field === 'field' && existsKeys.includes(value)) {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
field:
|
||||
type === MetadataType.Setting
|
||||
? t('knowledgeDetails.metadata.fieldExists')
|
||||
: t('knowledgeDetails.metadata.fieldNameExists'),
|
||||
};
|
||||
});
|
||||
} else if (field === 'field' && !existsKeys.includes(value)) {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
field: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
setMetaData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
},
|
||||
[existsKeys, type, t],
|
||||
);
|
||||
|
||||
// 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 (type === MetadataType.Setting && valueError.field) {
|
||||
return;
|
||||
}
|
||||
if (!metaData.restrictDefinedValues && isShowValueSwitch) {
|
||||
const newMetaData = { ...metaData, values: [] };
|
||||
onSave(newMetaData);
|
||||
} else {
|
||||
onSave(metaData);
|
||||
}
|
||||
handleHideModal();
|
||||
}, [metaData, onSave, handleHideModal, isShowValueSwitch, type, valueError]);
|
||||
|
||||
// Handle value changes, only update temporary state
|
||||
const handleValueChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setTempValues((prev) => {
|
||||
if (prev.includes(value)) {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
values: t('knowledgeDetails.metadata.valueExists'),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
values: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
const newValues = [...prev];
|
||||
newValues[index] = value;
|
||||
|
||||
return newValues;
|
||||
});
|
||||
},
|
||||
[t],
|
||||
);
|
||||
|
||||
// Handle blur event, synchronize to main state
|
||||
const handleValueBlur = useCallback(() => {
|
||||
// addUpdateValue(metaData.field, [...new Set([...tempValues])]);
|
||||
tempValues.forEach((newValue, index) => {
|
||||
if (index < data.values.length) {
|
||||
const originalValue = data.values[index];
|
||||
if (originalValue !== newValue) {
|
||||
addUpdateValue(metaData.field, originalValue, newValue);
|
||||
}
|
||||
} else {
|
||||
if (newValue) {
|
||||
addUpdateValue(metaData.field, '', newValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
handleChange('values', [...new Set([...tempValues])]);
|
||||
}, [handleChange, tempValues, metaData, data, 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],
|
||||
);
|
||||
|
||||
const showDeleteModal = (item: string, callback: () => void) => {
|
||||
setDeleteDialogContent({
|
||||
visible: true,
|
||||
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.value'),
|
||||
name: item,
|
||||
warnText: MetadataDeleteMap(t)[type as MetadataType].warnValueText,
|
||||
onOk: () => {
|
||||
hideDeleteModal();
|
||||
callback();
|
||||
},
|
||||
onCancel: () => {
|
||||
hideDeleteModal();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Handle adding new value
|
||||
const handleAddValue = useCallback(() => {
|
||||
setTempValues((prev) => [...new Set([...prev, ''])]);
|
||||
|
||||
// Synchronize to main state
|
||||
setMetaData((prev) => ({
|
||||
...prev,
|
||||
values: [...new Set([...prev.values, ''])],
|
||||
}));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
metaData,
|
||||
tempValues,
|
||||
valueError,
|
||||
deleteDialogContent,
|
||||
handleChange,
|
||||
handleValueChange,
|
||||
handleValueBlur,
|
||||
handleDelete,
|
||||
handleAddValue,
|
||||
showDeleteModal,
|
||||
handleSave,
|
||||
handleHideModal,
|
||||
};
|
||||
};
|
||||
@ -50,7 +50,11 @@ export interface IManageValuesProps {
|
||||
type: MetadataType;
|
||||
hideModal: () => void;
|
||||
onSave: (data: IMetaDataTableData) => void;
|
||||
addUpdateValue: (key: string, value: string | string[]) => void;
|
||||
addUpdateValue: (
|
||||
key: string,
|
||||
originalValue: string,
|
||||
newValue: string,
|
||||
) => void;
|
||||
addDeleteValue: (key: string, value: string) => void;
|
||||
}
|
||||
|
||||
@ -61,7 +65,8 @@ interface DeleteOperation {
|
||||
|
||||
interface UpdateOperation {
|
||||
key: string;
|
||||
value: string | string[];
|
||||
match: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface MetadataOperations {
|
||||
|
||||
@ -25,11 +25,16 @@ import {
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { Plus, Settings, Trash2 } from 'lucide-react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MetadataType, useManageMetaDataModal } from './hook';
|
||||
import {
|
||||
MetadataDeleteMap,
|
||||
MetadataType,
|
||||
useManageMetaDataModal,
|
||||
} from './hooks/use-manage-modal';
|
||||
import { IManageModalProps, IMetaDataTableData } from './interface';
|
||||
import { ManageValuesModal } from './manage-values-modal';
|
||||
|
||||
export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
const {
|
||||
title,
|
||||
@ -134,7 +139,8 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
const values = row.getValue('values') as Array<string>;
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{values.length > 0 &&
|
||||
{Array.isArray(values) &&
|
||||
values.length > 0 &&
|
||||
values
|
||||
.filter((value: string, index: number) => index < 2)
|
||||
?.map((value: string) => {
|
||||
@ -159,17 +165,12 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
title:
|
||||
t('common.delete') +
|
||||
' ' +
|
||||
t('knowledgeDetails.metadata.metadata'),
|
||||
name: row.getValue('field') + '/' + value,
|
||||
warnText: t(
|
||||
'knowledgeDetails.metadata.deleteWarn',
|
||||
{
|
||||
field:
|
||||
t('knowledgeDetails.metadata.field') +
|
||||
'/' +
|
||||
t('knowledgeDetails.metadata.values'),
|
||||
},
|
||||
),
|
||||
t('knowledgeDetails.metadata.value'),
|
||||
name: value,
|
||||
warnText:
|
||||
MetadataDeleteMap(t)[
|
||||
metadataType as MetadataType
|
||||
].warnValueText,
|
||||
onOk: () => {
|
||||
hideDeleteModal();
|
||||
handleDeleteSingleValue(
|
||||
@ -190,7 +191,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
{values.length > 2 && (
|
||||
{Array.isArray(values) && values.length > 2 && (
|
||||
<div className="text-text-secondary self-end">...</div>
|
||||
)}
|
||||
</div>
|
||||
@ -221,13 +222,14 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
setDeleteDialogContent({
|
||||
visible: true,
|
||||
title:
|
||||
t('common.delete') +
|
||||
' ' +
|
||||
t('knowledgeDetails.metadata.metadata'),
|
||||
// t('common.delete') +
|
||||
// ' ' +
|
||||
// t('knowledgeDetails.metadata.metadata')
|
||||
MetadataDeleteMap(t)[metadataType as MetadataType].title,
|
||||
name: row.getValue('field'),
|
||||
warnText: t('knowledgeDetails.metadata.deleteWarn', {
|
||||
field: t('knowledgeDetails.metadata.field'),
|
||||
}),
|
||||
warnText:
|
||||
MetadataDeleteMap(t)[metadataType as MetadataType]
|
||||
.warnFieldText,
|
||||
onOk: () => {
|
||||
hideDeleteModal();
|
||||
handleDeleteSingleRow(row.getValue('field'));
|
||||
@ -266,7 +268,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
manualPagination: true,
|
||||
});
|
||||
|
||||
const [shouldSave, setShouldSave] = useState(false);
|
||||
const handleSaveValues = (data: IMetaDataTableData) => {
|
||||
setTableData((prev) => {
|
||||
let newData;
|
||||
@ -300,8 +302,20 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
|
||||
return Array.from(fieldMap.values());
|
||||
});
|
||||
setShouldSave(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldSave) {
|
||||
const timer = setTimeout(() => {
|
||||
handleSave({ callback: () => {} });
|
||||
setShouldSave(false);
|
||||
}, 0);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [tableData, shouldSave, handleSave]);
|
||||
|
||||
const existsKeys = useMemo(() => {
|
||||
return tableData.map((item) => item.field);
|
||||
}, [tableData]);
|
||||
@ -386,7 +400,8 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
<ManageValuesModal
|
||||
title={
|
||||
<div>
|
||||
{metadataType === MetadataType.Setting
|
||||
{metadataType === MetadataType.Setting ||
|
||||
metadataType === MetadataType.SingleFileSetting
|
||||
? t('knowledgeDetails.metadata.fieldSetting')
|
||||
: t('knowledgeDetails.metadata.editMetadata')}
|
||||
</div>
|
||||
|
||||
@ -9,10 +9,10 @@ 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 { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MetadataType } from './hook';
|
||||
import { IManageValuesProps, IMetaDataTableData } from './interface';
|
||||
import { useManageValues } from './hooks/use-manage-values-modal';
|
||||
import { IManageValuesProps } from './interface';
|
||||
|
||||
// Create a separate input component, wrapped with memo to avoid unnecessary re-renders
|
||||
const ValueInputItem = memo(
|
||||
@ -57,188 +57,28 @@ const ValueInputItem = memo(
|
||||
export const ManageValuesModal = (props: IManageValuesProps) => {
|
||||
const {
|
||||
title,
|
||||
data,
|
||||
isEditField,
|
||||
visible,
|
||||
isAddValue,
|
||||
isShowDescription,
|
||||
isShowValueSwitch,
|
||||
isVerticalShowValue,
|
||||
hideModal,
|
||||
onSave,
|
||||
addUpdateValue,
|
||||
addDeleteValue,
|
||||
existsKeys,
|
||||
type,
|
||||
} = props;
|
||||
const [metaData, setMetaData] = useState(data);
|
||||
const {
|
||||
metaData,
|
||||
tempValues,
|
||||
valueError,
|
||||
deleteDialogContent,
|
||||
handleChange,
|
||||
handleValueChange,
|
||||
handleValueBlur,
|
||||
handleDelete,
|
||||
handleAddValue,
|
||||
showDeleteModal,
|
||||
handleSave,
|
||||
handleHideModal,
|
||||
} = useManageValues(props);
|
||||
const { t } = useTranslation();
|
||||
const [valueError, setValueError] = useState<Record<string, string>>({
|
||||
field: '',
|
||||
values: '',
|
||||
});
|
||||
const [deleteDialogContent, setDeleteDialogContent] = useState({
|
||||
visible: false,
|
||||
title: '',
|
||||
name: '',
|
||||
warnText: '',
|
||||
onOk: () => {},
|
||||
onCancel: () => {},
|
||||
});
|
||||
const hideDeleteModal = () => {
|
||||
setDeleteDialogContent({
|
||||
visible: false,
|
||||
title: '',
|
||||
name: '',
|
||||
warnText: '',
|
||||
onOk: () => {},
|
||||
onCancel: () => {},
|
||||
});
|
||||
};
|
||||
|
||||
// Use functional update to avoid closure issues
|
||||
const handleChange = useCallback(
|
||||
(field: string, value: any) => {
|
||||
if (field === 'field' && existsKeys.includes(value)) {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
field:
|
||||
type === MetadataType.Setting
|
||||
? t('knowledgeDetails.metadata.fieldExists')
|
||||
: t('knowledgeDetails.metadata.fieldNameExists'),
|
||||
};
|
||||
});
|
||||
} else if (field === 'field' && !existsKeys.includes(value)) {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
field: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
setMetaData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
},
|
||||
[existsKeys, type, t],
|
||||
);
|
||||
|
||||
// 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 (type === MetadataType.Setting && valueError.field) {
|
||||
return;
|
||||
}
|
||||
if (!metaData.restrictDefinedValues && isShowValueSwitch) {
|
||||
const newMetaData = { ...metaData, values: [] };
|
||||
onSave(newMetaData);
|
||||
} else {
|
||||
onSave(metaData);
|
||||
}
|
||||
handleHideModal();
|
||||
}, [metaData, onSave, handleHideModal, isShowValueSwitch, type, valueError]);
|
||||
|
||||
// Handle value changes, only update temporary state
|
||||
const handleValueChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setTempValues((prev) => {
|
||||
if (prev.includes(value)) {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
values: t('knowledgeDetails.metadata.valueExists'),
|
||||
};
|
||||
});
|
||||
} else {
|
||||
setValueError((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
values: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
const newValues = [...prev];
|
||||
newValues[index] = value;
|
||||
|
||||
return newValues;
|
||||
});
|
||||
},
|
||||
[t],
|
||||
);
|
||||
|
||||
// Handle blur event, synchronize to main state
|
||||
const handleValueBlur = useCallback(() => {
|
||||
addUpdateValue(metaData.field, [...new Set([...tempValues])]);
|
||||
handleChange('values', [...new Set([...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],
|
||||
);
|
||||
|
||||
const showDeleteModal = (item: string, callback: () => void) => {
|
||||
setDeleteDialogContent({
|
||||
visible: true,
|
||||
title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'),
|
||||
name: metaData.field + '/' + item,
|
||||
warnText: t('knowledgeDetails.metadata.deleteWarn', {
|
||||
field:
|
||||
t('knowledgeDetails.metadata.field') +
|
||||
'/' +
|
||||
t('knowledgeDetails.metadata.values'),
|
||||
}),
|
||||
onOk: () => {
|
||||
hideDeleteModal();
|
||||
callback();
|
||||
},
|
||||
onCancel: () => {
|
||||
hideDeleteModal();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Handle adding new value
|
||||
const handleAddValue = useCallback(() => {
|
||||
setTempValues((prev) => [...new Set([...prev, ''])]);
|
||||
|
||||
// Synchronize to main state
|
||||
setMetaData((prev) => ({
|
||||
...prev,
|
||||
values: [...new Set([...prev.values, ''])],
|
||||
}));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
Reference in New Issue
Block a user