diff --git a/web/app/(commonLayout)/datasets/Container.tsx b/web/app/(commonLayout)/datasets/Container.tsx index 9df1f5f524..a9e5e4a813 100644 --- a/web/app/(commonLayout)/datasets/Container.tsx +++ b/web/app/(commonLayout)/datasets/Container.tsx @@ -22,6 +22,8 @@ import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/d import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' import CreateModal from '@/app/components/datasets/metadata/create-metadata-modal' import SelectMetadataModal from '@/app/components/datasets/metadata/select-metadata-modal' +import DatasetMetadataDrawer from '@/app/components/datasets/metadata/dataset-metadata-drawer' + // Services import { fetchDatasetApiBaseUrl } from '@/service/datasets' @@ -30,6 +32,7 @@ import { useTabSearchParams } from '@/hooks/use-tab-searchparams' import { useStore as useTagStore } from '@/app/components/base/tag-management/store' import { useAppContext } from '@/context/app-context' import { useExternalApiPanel } from '@/context/external-api-panel-context' +import { DataType } from '@/app/components/datasets/metadata/types' const Container = () => { const { t } = useTranslation() @@ -82,11 +85,32 @@ const Container = () => { return router.replace('/apps') }, [currentWorkspace, router]) + const [userMetadata, setUserMetadata] = useState([ + { id: '1', name: 'name1', type: DataType.string, valueLength: 1 }, + { id: '2', name: 'name2', type: DataType.number, valueLength: 2 }, + { id: '3', name: 'name3', type: DataType.time, valueLength: 3 }, + ]) + return (
select} onSave={(data) => { console.log(data) }} /> add} hasBack onSave={(data) => { console.log(data) }} /> + + { }} + onClose={() => { }} + />
<> - {title && - {title} - } - {showClose && - - } +
+ {title && + {title} + } + {showClose && + + } +
+ {description && {description}} {children} diff --git a/web/app/components/datasets/metadata/dataset-metadata-drawer.tsx b/web/app/components/datasets/metadata/dataset-metadata-drawer.tsx new file mode 100644 index 0000000000..91f2899486 --- /dev/null +++ b/web/app/components/datasets/metadata/dataset-metadata-drawer.tsx @@ -0,0 +1,187 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback, useState } from 'react' +import type { MetadataItemWithValueLength } from './types' +import Drawer from '../../base/drawer' +import Button from '@/app/components/base/button' +import { RiAddLine, RiDeleteBinLine, RiEditLine } from '@remixicon/react' +import { getIcon } from './utils/get-icon' +import cn from '@/utils/classnames' +import Modal from '../../base/modal' +import Field from './field' +import Input from '@/app/components/base/input' +import { useTranslation } from 'react-i18next' +import produce from 'immer' +import Switch from '../../base/switch' +import Tooltip from '../../base/tooltip' +import CreateModal from '@/app/components/datasets/metadata/create-metadata-modal' + +const i18nPrefix = 'dataset.metadata.datasetMetadata' + +type Props = { + userMetadata: MetadataItemWithValueLength[] + builtInMetadata: MetadataItemWithValueLength[] + isBuiltInEnabled: boolean + onIsBuiltInEnabledChange: (value: boolean) => void + onClose: () => void + onChange: (data: MetadataItemWithValueLength[]) => void +} + +type ItemProps = { + readonly?: boolean + payload: MetadataItemWithValueLength + onRename?: () => void + onDelete?: () => void +} +const Item: FC = ({ + readonly, + payload, + onRename, + onDelete, +}) => { + const Icon = getIcon(payload.type) + + const handleRename = useCallback(() => { + onRename?.() + }, [onRename]) + + const handleDelete = useCallback(() => { + onDelete?.() + }, [onDelete]) + + return ( +
+
+ +
{payload.name}
+
+
+ {payload.valueLength || 0} values +
+
+ + +
+
+ ) +} + +const DatasetMetadataDrawer: FC = ({ + userMetadata, + builtInMetadata, + isBuiltInEnabled, + onIsBuiltInEnabledChange, + onClose, + onChange, +}) => { + const { t } = useTranslation() + const [isShowRenameModal, setIsShowRenameModal] = useState(false) + const [currPayload, setCurrPayload] = useState(null) + const [templeName, setTempleName] = useState('') + const handleRename = useCallback((payload: MetadataItemWithValueLength) => { + return () => { + setCurrPayload(payload) + setTempleName(payload.name) + setIsShowRenameModal(true) + } + }, [setCurrPayload, setIsShowRenameModal]) + + const handleAdd = useCallback((data: MetadataItemWithValueLength) => { + const nextUserMetadata = produce(userMetadata, (draft) => { + draft.push(data) + }) + onChange(nextUserMetadata) + }, [userMetadata, onChange]) + + const handleRenamed = useCallback(() => { + const nextUserMetadata = produce(userMetadata, (draft) => { + const index = draft.findIndex(p => p.id === currPayload?.id) + if (index !== -1) + draft[index].name = templeName! + }) + + onChange(nextUserMetadata) + setIsShowRenameModal(false) + }, [currPayload, templeName, userMetadata, onChange]) + + const handleDelete = useCallback((payload: MetadataItemWithValueLength) => { + return () => { + const nextUserMetadata = userMetadata.filter(p => p.id !== payload.id) + onChange(nextUserMetadata) + } + }, [userMetadata, onChange]) + + return ( + +
You can manage all metadata in this knowledge here. Modifications will be synchronized to every document.
+ + + + Add Metadata + } hasBack onSave={handleAdd} /> + + {userMetadata.map(payload => ( + + ))} + +
+ +
Built-in
+ +
+ + {builtInMetadata.map(payload => ( + + ))} + + {isShowRenameModal && ( + + + setTempleName(e.target.value)} + placeholder={t(`${i18nPrefix}.namePlaceholder`)} + /> + +
+ + +
+
+ )} + +
+ ) +} +export default React.memo(DatasetMetadataDrawer) diff --git a/web/app/components/datasets/metadata/select-metadata.tsx b/web/app/components/datasets/metadata/select-metadata.tsx index 641c79419f..9e41512980 100644 --- a/web/app/components/datasets/metadata/select-metadata.tsx +++ b/web/app/components/datasets/metadata/select-metadata.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import React, { useState } from 'react' -import { DataType } from './types' import type { MetadataItem } from './types' import SearchInput from '../../base/search-input' -import { RiAddLine, RiArrowRightUpLine, RiHashtag, RiTextSnippet, RiTimeLine } from '@remixicon/react' +import { RiAddLine, RiArrowRightUpLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' +import { getIcon } from './utils/get-icon' const i18nPrefix = 'dataset.metadata.selectMetadata' @@ -16,14 +16,6 @@ type Props = { onManage: () => void } -const getIcon = (type: DataType) => { - return ({ - [DataType.string]: RiTextSnippet, - [DataType.number]: RiHashtag, - [DataType.time]: RiTimeLine, - }[type] || RiTextSnippet) -} - const SelectMetadata: FC = ({ list, onSelect, diff --git a/web/app/components/datasets/metadata/types.ts b/web/app/components/datasets/metadata/types.ts index 5d58c1995c..ee44a1ce91 100644 --- a/web/app/components/datasets/metadata/types.ts +++ b/web/app/components/datasets/metadata/types.ts @@ -9,3 +9,7 @@ export type MetadataItem = { type: DataType name: string } + +export type MetadataItemWithValueLength = MetadataItem & { + valueLength: number +} diff --git a/web/app/components/datasets/metadata/utils/get-icon.ts b/web/app/components/datasets/metadata/utils/get-icon.ts new file mode 100644 index 0000000000..69b9bb18dc --- /dev/null +++ b/web/app/components/datasets/metadata/utils/get-icon.ts @@ -0,0 +1,10 @@ +import { DataType } from '../types' +import { RiHashtag, RiTextSnippet, RiTimeLine } from '@remixicon/react' + +export const getIcon = (type: DataType) => { + return ({ + [DataType.string]: RiTextSnippet, + [DataType.number]: RiHashtag, + [DataType.time]: RiTimeLine, + }[type] || RiTextSnippet) +}