diff --git a/web/app/components/datasets/documents/list.tsx b/web/app/components/datasets/documents/list.tsx index 898fbb25db..af8a3adb07 100644 --- a/web/app/components/datasets/documents/list.tsx +++ b/web/app/components/datasets/documents/list.tsx @@ -426,7 +426,13 @@ const DocumentList: FC = ({ const isQAMode = chunkingMode === ChunkingMode.qa const [localDocs, setLocalDocs] = useState(documents) const [enableSort, setEnableSort] = useState(true) - const { isShowEditModal, showEditModal, hideEditModal } = useBatchEditDocumentMetadata({ + const { + isShowEditModal, + showEditModal, + hideEditModal, + originalList, + handleSave, + } = useBatchEditDocumentMetadata({ list: documents, }) @@ -655,8 +661,8 @@ const DocumentList: FC = ({ {isShowEditModal && ( { }} + list={originalList} + onSave={handleSave} onHide={hideEditModal} /> )} diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx index d5d511dbb3..294814e72b 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import React, { useCallback, useState } from 'react' import Modal from '../../../base/modal' -import { DataType, type MetadataItemWithEdit } from '../types' +import type { MetadataItemInBatchEdit } from '../types' +import { DataType, type MetadataItemWithEdit, UpdateType } from '../types' import EditMetadataBatchItem from './edit-row' import AddedMetadataItem from './add-row' import Button from '../../../base/button' @@ -13,30 +14,45 @@ import SelectMetadataModal from '../metadata-dataset/select-metadata-modal' import { RiQuestionLine } from '@remixicon/react' import Divider from '@/app/components/base/divider' import AddMetadataButton from '../add-metadata-button' +import produce from 'immer' const i18nPrefix = 'dataset.metadata.batchEditMetadata' type Props = { documentNum: number - list: MetadataItemWithEdit[] - onChange: (list: MetadataItemWithEdit[], addedList: MetadataItemWithEdit[], isApplyToAllSelectDocument: boolean) => void + list: MetadataItemInBatchEdit[] + onSave: (list: MetadataItemInBatchEdit[], isApplyToAllSelectDocument: boolean) => void onHide: () => void } const EditMetadataBatchModal: FC = ({ documentNum, list, - onChange, + onSave, onHide, }) => { const { t } = useTranslation() const [templeList, setTempleList] = useState(list) const handleTemplesChange = useCallback((payload: MetadataItemWithEdit) => { - const newTempleList = templeList.map(i => i.id === payload.id ? payload : i) + const newTempleList = produce(templeList, (draft) => { + const index = draft.findIndex(i => i.id === payload.id) + if (index !== -1) { + draft[index] = payload + draft[index].isUpdated = true + draft[index].updateType = UpdateType.changeValue + } + }, + ) setTempleList(newTempleList) }, [templeList]) const handleTempleItemRemove = useCallback((id: string) => { - const newTempleList = templeList.filter(i => i.id !== id) + const newTempleList = produce(templeList, (draft) => { + const index = draft.findIndex(i => i.id === id) + if (index !== -1) { + draft[index].isUpdated = true + draft[index].updateType = UpdateType.delete + } + }) setTempleList(newTempleList) }, [templeList]) @@ -63,8 +79,8 @@ const EditMetadataBatchModal: FC = ({ const [isApplyToAllSelectDocument, setIsApplyToAllSelectDocument] = useState(false) const handleSave = useCallback(() => { - onChange(templeList, addedList, isApplyToAllSelectDocument) - }, [templeList, addedList, isApplyToAllSelectDocument, onChange]) + onSave([...templeList.filter(item => item.updateType !== UpdateType.delete), ...addedList], isApplyToAllSelectDocument) + }, [templeList, addedList, isApplyToAllSelectDocument, onSave]) return ( { + const originalList: MetadataItemInBatchEdit[] = useMemo(() => { const idNameValue: Record = {} // TODO: mock backend data struct // const metaDataList: MetadataItemWithValue[][] = list.map((item, i) => { @@ -72,13 +72,63 @@ const useBatchEditDocumentMetadata = ({ }) }) return res - }, [list]) + }, []) + + const formateToBackendList = (editedList: MetadataItemInBatchEdit[], isApplyToAllSelectDocument: boolean) => { + const updatedList = editedList.filter((editedItem) => { + const originalItem = originalList.find(i => i.id === editedItem.id) + if (!originalItem) // added item + return true + if (editedItem.value !== originalItem.value) + return true + return false + }) + const removedList = originalList.filter((originalItem) => { + const editedItem = editedList.find(i => i.id === originalItem.id) + if (!editedItem) // removed item + return true + return false + }) + + const res: { document_id: string, metadata_list: MetadataItemWithValue[] }[] = list.map((item, i) => { + // the new metadata will override the old one + const oldMetadataList = item.doc_metadata || testMetadataList[i] // TODO: used mock data + const newMetadataList: MetadataItemWithValue[] = oldMetadataList + .filter((item) => { + return !removedList.find(removedItem => removedItem.id === item.id) + }) + .map((item) => { + const editedItem = updatedList.find(i => i.id === item.id) + if (editedItem) + return editedItem + return item + }) + if (isApplyToAllSelectDocument) { + // add missing metadata item + updatedList.forEach((editedItem) => { + if (!newMetadataList.find(i => i.id === editedItem.id)) + newMetadataList.push(editedItem) + }) + } + return { + document_id: item.id, + metadata_list: newMetadataList, + } + }).filter(item => item.metadata_list.length > 0) + return res + } + + const handleSave = (editedList: MetadataItemInBatchEdit[], isApplyToAllSelectDocument: boolean) => { + const backendList = formateToBackendList(editedList, isApplyToAllSelectDocument) + console.log(backendList) + } return { isShowEditModal, showEditModal, hideEditModal, originalList, + handleSave, } }