Files
dify/web/app/components/datasets/documents/detail/metadata/index.tsx
CodingOnStar a25e4c1b3a refactor(web): restructure metadata components and add tests
This commit refactors the metadata components within the document detail section by extracting and organizing them into separate files for better maintainability and readability. The following components were created or modified:

- DocTypeSelector: A new component for selecting document types.
- FieldInfo: A new component for displaying and editing field information.
- IconButton: A new component for rendering document type icons.
- MetadataFieldList: A new component for rendering a list of metadata fields.
- TypeIcon: A new component for displaying icons based on document types.
- Utility functions for mapping options.

Additionally, comprehensive tests were added for each new component to ensure functionality and reliability.
2026-01-30 10:09:58 +08:00

177 lines
5.4 KiB
TypeScript

'use client'
import type { FC } from 'react'
import type { metadataType } from '@/hooks/use-metadata'
import type { FullDocumentDetail } from '@/models/datasets'
import { PencilIcon } from '@heroicons/react/24/outline'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import { useMetadataMap } from '@/hooks/use-metadata'
import { useDocumentContext } from '../context'
import { DocTypeSelector, MetadataFieldList, TypeIcon } from './components'
import { useMetadataEditor, useMetadataSave } from './hooks'
import s from './style.module.css'
export { FieldInfo } from './components'
type MetadataProps = {
docDetail?: FullDocumentDetail
loading: boolean
onUpdate: () => void
}
const Metadata: FC<MetadataProps> = ({ docDetail, loading, onUpdate }) => {
const { t } = useTranslation()
const metadataMap = useMetadataMap()
const datasetId = useDocumentContext(state => state.datasetId)
const documentId = useDocumentContext(state => state.documentId)
const {
doc_type,
editStatus,
setEditStatus,
metadataParams,
showDocTypes,
tempDocType,
setTempDocType,
confirmDocType,
cancelDocType,
enableEdit,
resetToInitial,
updateMetadataField,
openDocTypeSelector,
} = useMetadataEditor({ docDetail })
const { saveLoading, handleSave } = useMetadataSave({
datasetId,
documentId,
metadataParams,
doc_type,
onSuccess: () => setEditStatus(false),
onUpdate,
})
const renderDocTypeDisplay = () => {
const docTypeKey = (doc_type || 'book') as metadataType
if (!editStatus) {
return (
<div className={s.documentTypeShow}>
<TypeIcon iconName={metadataMap[docTypeKey]?.iconName || ''} className={s.iconShow} />
{metadataMap[docTypeKey].text}
</div>
)
}
if (showDocTypes)
return null
return (
<div className={s.documentTypeShow}>
{metadataParams.documentType && (
<>
<TypeIcon iconName={metadataMap[metadataParams.documentType || 'book'].iconName || ''} className={s.iconShow} />
{metadataMap[metadataParams.documentType || 'book'].text}
{editStatus && (
<div className="ml-1 inline-flex items-center gap-1">
·
<div
onClick={openDocTypeSelector}
className="cursor-pointer hover:text-text-accent"
>
{t('operation.change', { ns: 'common' })}
</div>
</div>
)}
</>
)}
</div>
)
}
const renderHeaderActions = () => {
if (!editStatus) {
return (
<Button onClick={enableEdit} className={`${s.opBtn} ${s.opEditBtn}`}>
<PencilIcon className={s.opIcon} />
{t('operation.edit', { ns: 'common' })}
</Button>
)
}
if (showDocTypes)
return null
return (
<div className={s.opBtnWrapper}>
<Button onClick={resetToInitial} className={`${s.opBtn} ${s.opCancelBtn}`}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button
onClick={handleSave}
className={`${s.opBtn} ${s.opSaveBtn}`}
variant="primary"
loading={saveLoading}
>
{t('operation.save', { ns: 'common' })}
</Button>
</div>
)
}
return (
<div className={`${s.main} ${editStatus ? 'bg-white' : 'bg-gray-25'}`}>
{loading
? (<Loading type="app" />)
: (
<>
<div className={s.titleWrapper}>
<span className={s.title}>{t('metadata.title', { ns: 'datasetDocuments' })}</span>
{renderHeaderActions()}
</div>
{renderDocTypeDisplay()}
{(!doc_type && showDocTypes) ? null : <Divider />}
{showDocTypes
? (
<DocTypeSelector
documentType={metadataParams.documentType}
tempDocType={tempDocType}
doc_type={doc_type}
onTempDocTypeChange={setTempDocType}
onConfirm={confirmDocType}
onCancel={cancelDocType}
/>
)
: (
<MetadataFieldList
mainField={metadataParams.documentType || ''}
canEdit={editStatus}
docDetail={docDetail}
metadataParams={metadataParams}
onUpdateField={updateMetadataField}
/>
)}
<Divider />
<MetadataFieldList
mainField="originInfo"
canEdit={false}
docDetail={docDetail}
metadataParams={metadataParams}
onUpdateField={updateMetadataField}
/>
<div className={`${s.title} mt-8`}>{metadataMap.technicalParameters.text}</div>
<Divider />
<MetadataFieldList
mainField="technicalParameters"
canEdit={false}
docDetail={docDetail}
metadataParams={metadataParams}
onUpdateField={updateMetadataField}
/>
</>
)}
</div>
)
}
export default Metadata