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.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 => (
+
+ ))}
+
+
+
+ {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)
+}