| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 | 
							- 'use client'
 
- import type { FC } from 'react'
 
- import React, { useState, useEffect } from 'react'
 
- import { PencilIcon } from '@heroicons/react/24/outline'
 
- import { useTranslation } from 'react-i18next'
 
- import { useContext } from 'use-context-selector'
 
- import { get } from 'lodash-es'
 
- import cn from 'classnames'
 
- import Input from '@/app/components/base/input'
 
- import Button from '@/app/components/base/button'
 
- import Tooltip from '@/app/components/base/tooltip'
 
- import Radio from '@/app/components/base/radio'
 
- import Divider from '@/app/components/base/divider'
 
- import { ToastContext } from '@/app/components/base/toast'
 
- import { SimpleSelect } from '@/app/components/base/select'
 
- import Loading from '@/app/components/base/loading'
 
- import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
 
- import { asyncRunSafe, getTextWidthWithCanvas } from '@/utils'
 
- import { modifyDocMetadata } from '@/service/datasets'
 
- import type { CommonResponse } from '@/models/common'
 
- import type { FullDocumentDetail, DocType } from '@/models/datasets'
 
- import { CUSTOMIZABLE_DOC_TYPES } from '@/models/datasets'
 
- import type { metadataType, inputType } from '@/hooks/use-metadata'
 
- import { useMetadataMap, useLanguages, useBookCategories, usePersonalDocCategories, useBusinessDocCategories } from '@/hooks/use-metadata'
 
- import { DocumentContext } from '../index'
 
- import s from './style.module.css'
 
- const map2Options = (map: { [key: string]: string }) => {
 
-   return Object.keys(map).map(key => ({ value: key, name: map[key] }))
 
- }
 
- type IFieldInfoProps = {
 
-   label: string
 
-   value?: string
 
-   displayedValue?: string
 
-   defaultValue?: string
 
-   showEdit?: boolean
 
-   inputType?: inputType
 
-   selectOptions?: Array<{ value: string; name: string }>
 
-   onUpdate?: (v: any) => void
 
- }
 
- export const FieldInfo: FC<IFieldInfoProps> = ({
 
-   label,
 
-   value = '',
 
-   displayedValue = '',
 
-   defaultValue,
 
-   showEdit = false,
 
-   inputType = 'input',
 
-   selectOptions = [],
 
-   onUpdate
 
- }) => {
 
-   const { t } = useTranslation()
 
-   const textNeedWrap = getTextWidthWithCanvas(displayedValue) > 190
 
-   const editAlignTop = showEdit && inputType === 'textarea'
 
-   const readAlignTop = !showEdit && textNeedWrap
 
-   return (
 
-     <div className={cn(s.fieldInfo, editAlignTop && `!items-start`, readAlignTop && '!items-start pt-1')}>
 
-       <div className={cn(s.label, editAlignTop && 'pt-1')}>{label}</div>
 
-       <div className={s.value}>
 
-         {!showEdit
 
-           ? displayedValue
 
-           : inputType === 'select'
 
-             ? <SimpleSelect
 
-               onSelect={({ value }) => onUpdate && onUpdate(value as string)}
 
-               items={selectOptions}
 
-               defaultValue={value}
 
-               className={s.select}
 
-               wrapperClassName={s.selectWrapper}
 
-               placeholder={`${t('datasetDocuments.metadata.placeholder.select')}${label}`}
 
-             />
 
-             : inputType === 'textarea'
 
-               ? <AutoHeightTextarea
 
-                 onChange={e => onUpdate && onUpdate(e.target.value)}
 
-                 value={value}
 
-                 className={s.textArea}
 
-                 placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
 
-               />
 
-               : <Input
 
-                 className={s.input}
 
-                 onChange={onUpdate}
 
-                 value={value}
 
-                 defaultValue={defaultValue}
 
-                 placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
 
-               />
 
-         }
 
-       </div>
 
-     </div>
 
-   )
 
- }
 
- const TypeIcon: FC<{ iconName: string; className?: string }> = ({ iconName, className = '' }) => {
 
-   return <div className={cn(s.commonIcon, s[`${iconName}Icon`], className)}
 
-   />
 
- }
 
- const IconButton: FC<{
 
-   type: DocType
 
-   isChecked: boolean
 
- }> = ({ type, isChecked = false }) => {
 
-   const metadataMap = useMetadataMap()
 
-   return (
 
-     <Tooltip content={metadataMap[type].text} selector={`doc-metadata-${type}`}>
 
-       <button className={cn(s.iconWrapper, 'group', isChecked ? s.iconCheck : '')}>
 
-         <TypeIcon
 
-           iconName={metadataMap[type].iconName || ''}
 
-           className={`group-hover:bg-primary-600 ${isChecked ? '!bg-primary-600' : ''}`}
 
-         />
 
-       </button>
 
-     </Tooltip>
 
-   )
 
- }
 
- type IMetadataProps = {
 
-   docDetail?: FullDocumentDetail
 
-   loading: boolean
 
-   onUpdate: () => void
 
- }
 
- const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
 
-   const { doc_metadata = {} } = docDetail || {}
 
-   const doc_type = docDetail?.doc_type || ''
 
-   const { t } = useTranslation()
 
-   const metadataMap = useMetadataMap()
 
-   const languageMap = useLanguages()
 
-   const bookCategoryMap = useBookCategories()
 
-   const personalDocCategoryMap = usePersonalDocCategories()
 
-   const businessDocCategoryMap = useBusinessDocCategories()
 
-   const [editStatus, setEditStatus] = useState(!doc_type) // if no documentType, in editing status by default
 
-   // the initial values are according to the documentType
 
-   const [metadataParams, setMetadataParams] = useState<{
 
-     documentType?: DocType | '';
 
-     metadata: { [key: string]: string }
 
-   }>(
 
-     doc_type ? {
 
-       documentType: doc_type,
 
-       metadata: doc_metadata || {}
 
-     } : { metadata: {} })
 
-   const [showDocTypes, setShowDocTypes] = useState(!doc_type) // whether show doc types
 
-   const [tempDocType, setTempDocType] = useState<DocType | undefined | ''>('') // for remember icon click
 
-   const [saveLoading, setSaveLoading] = useState(false)
 
-   const { notify } = useContext(ToastContext)
 
-   const { datasetId = '', documentId = '' } = useContext(DocumentContext)
 
-   useEffect(() => {
 
-     if (docDetail?.doc_type) {
 
-       setEditStatus(false)
 
-       setShowDocTypes(false)
 
-       setTempDocType(docDetail?.doc_type)
 
-       setMetadataParams({
 
-         documentType: docDetail?.doc_type,
 
-         metadata: docDetail?.doc_metadata || {}
 
-       })
 
-     }
 
-   }, [docDetail?.doc_type])
 
-   // confirm doc type
 
-   const confirmDocType = () => {
 
-     if (!tempDocType) return;
 
-     setMetadataParams({
 
-       documentType: tempDocType,
 
-       metadata: tempDocType === metadataParams.documentType ? metadataParams.metadata : {} // change doc type, clear metadata
 
-     })
 
-     setEditStatus(true)
 
-     setShowDocTypes(false)
 
-   }
 
-   // cancel doc type
 
-   const cancelDocType = () => {
 
-     setTempDocType(metadataParams.documentType)
 
-     setEditStatus(true)
 
-     setShowDocTypes(false)
 
-   }
 
-   // show doc type select
 
-   const renderSelectDocType = () => {
 
-     const { documentType } = metadataParams
 
-     return (
 
-       <>
 
-         {!doc_type && !documentType && <>
 
-           <div className={s.desc}>{t('datasetDocuments.metadata.desc')}</div>
 
-         </>}
 
-         <div className={s.operationWrapper}>
 
-           {!doc_type && !documentType && <>
 
-             <span className={s.title}>{t('datasetDocuments.metadata.docTypeSelectTitle')}</span>
 
-           </>}
 
-           {documentType && <>
 
-             <span className={s.title}>{t('datasetDocuments.metadata.docTypeChangeTitle')}</span>
 
-             <span className={s.changeTip}>{t('datasetDocuments.metadata.docTypeSelectWarning')}</span>
 
-           </>}
 
-           <Radio.Group value={tempDocType ?? documentType} onChange={setTempDocType} className={s.radioGroup}>
 
-             {CUSTOMIZABLE_DOC_TYPES.map((type) => {
 
-               const currValue = tempDocType ?? documentType
 
-               return <Radio value={type} className={`${s.radio} ${currValue === type ? 'shadow-none' : ''}`}>
 
-                 <IconButton
 
-                   type={type}
 
-                   isChecked={currValue === type}
 
-                 />
 
-               </Radio>
 
-             })}
 
-           </Radio.Group>
 
-           {!doc_type && !documentType && (
 
-             <Button type='primary'
 
-               onClick={confirmDocType}
 
-               disabled={!tempDocType}
 
-             >
 
-               {t('datasetDocuments.metadata.firstMetaAction')}
 
-             </Button>
 
-           )}
 
-           {documentType && <div className={s.opBtnWrapper}>
 
-             <Button onClick={confirmDocType} className={`${s.opBtn} ${s.opSaveBtn}`} type='primary' >{t('common.operation.save')}</Button>
 
-             <Button onClick={cancelDocType} className={`${s.opBtn} ${s.opCancelBtn}`}>{t('common.operation.cancel')}</Button>
 
-           </div>}
 
-         </div >
 
-       </>
 
-     )
 
-   }
 
-   // show metadata info and edit
 
-   const renderFieldInfos = ({ mainField = 'book', canEdit }: { mainField?: metadataType | ''; canEdit?: boolean }) => {
 
-     if (!mainField) return null
 
-     const fieldMap = metadataMap[mainField]?.subFieldsMap
 
-     const sourceData = ['originInfo', 'technicalParameters'].includes(mainField) ? docDetail : metadataParams.metadata
 
-     const getTargetMap = (field: string) => {
 
-       if (field === 'language') return languageMap
 
-       if (field === 'category' && mainField === 'book') {
 
-         return bookCategoryMap
 
-       }
 
-       if (field === 'document_type') {
 
-         if (mainField === 'personal_document') return personalDocCategoryMap
 
-         if (mainField === 'business_document') return businessDocCategoryMap
 
-       }
 
-       return {} as any
 
-     }
 
-     const getTargetValue = (field: string) => {
 
-       const val = get(sourceData, field, '')
 
-       if (!val && val !== 0) return '-'
 
-       if (fieldMap[field]?.inputType === 'select')
 
-         return getTargetMap(field)[val]
 
-       if (fieldMap[field]?.render)
 
-         return fieldMap[field]?.render?.(val, field === 'hit_count' ? get(sourceData, 'segment_count', 0) as number : undefined)
 
-       return val
 
-     }
 
-     return <div className='flex flex-col gap-1'>
 
-       {Object.keys(fieldMap).map((field) => {
 
-         return <FieldInfo
 
-           key={fieldMap[field]?.label}
 
-           label={fieldMap[field]?.label}
 
-           displayedValue={getTargetValue(field)}
 
-           value={get(sourceData, field, '')}
 
-           inputType={fieldMap[field]?.inputType || 'input'}
 
-           showEdit={canEdit}
 
-           onUpdate={(val) => {
 
-             setMetadataParams(pre => ({ ...pre, metadata: { ...pre.metadata, [field]: val } }))
 
-           }}
 
-           selectOptions={map2Options(getTargetMap(field))}
 
-         />
 
-       })}
 
-     </div>
 
-   }
 
-   const enabledEdit = () => {
 
-     setEditStatus(true)
 
-   }
 
-   const onCancel = () => {
 
-     setMetadataParams({ documentType: doc_type || '', metadata: { ...(docDetail?.doc_metadata || {}) } })
 
-     setEditStatus(!doc_type)
 
-     if (!doc_type)
 
-       setShowDocTypes(true)
 
-   }
 
-   const onSave = async () => {
 
-     console.log('metadataParams:', metadataParams)
 
-     setSaveLoading(true)
 
-     const [e] = await asyncRunSafe<CommonResponse>(modifyDocMetadata({
 
-       datasetId,
 
-       documentId,
 
-       body: {
 
-         doc_type: metadataParams.documentType || doc_type || '',
 
-         doc_metadata: metadataParams.metadata
 
-       }
 
-     }) as Promise<CommonResponse>)
 
-     if (!e)
 
-       notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
 
-     else
 
-       notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
 
-     onUpdate?.()
 
-     setEditStatus(false)
 
-     setSaveLoading(false)
 
-   }
 
-   return (
 
-     <div className={`${s.main} ${editStatus ? 'bg-white' : 'bg-gray-25'}`}>
 
-       {loading ? <Loading type='app' /> : (
 
-         <>
 
-           <div className={s.titleWrapper}>
 
-             <span className={s.title}>{t('datasetDocuments.metadata.title')}</span>
 
-             {!editStatus
 
-               ? <Button onClick={enabledEdit} className={`${s.opBtn} ${s.opEditBtn}`}>
 
-                 <PencilIcon className={s.opIcon} />
 
-                 {t('common.operation.edit')}
 
-               </Button>
 
-               : showDocTypes
 
-                 ? null
 
-                 : <div className={s.opBtnWrapper}>
 
-                   <Button onClick={onCancel} className={`${s.opBtn} ${s.opCancelBtn}`}>{t('common.operation.cancel')}</Button>
 
-                   <Button onClick={onSave}
 
-                     className={`${s.opBtn} ${s.opSaveBtn}`}
 
-                     type='primary'
 
-                     loading={saveLoading}
 
-                   >
 
-                     {t('common.operation.save')}
 
-                   </Button>
 
-                 </div>}
 
-           </div>
 
-           {/* show selected doc type and changing entry */}
 
-           {!editStatus
 
-             ? <div className={s.documentTypeShow}>
 
-               <TypeIcon iconName={metadataMap[doc_type || 'book']?.iconName || ''} className={s.iconShow} />
 
-               {metadataMap[doc_type || 'book'].text}
 
-             </div>
 
-             : showDocTypes
 
-               ? null
 
-               : <div className={s.documentTypeShow}>
 
-                 {metadataParams.documentType && <>
 
-                   <TypeIcon iconName={metadataMap[metadataParams.documentType || 'book'].iconName || ''} className={s.iconShow} />
 
-                   {metadataMap[metadataParams.documentType || 'book'].text}
 
-                   {editStatus && <div className='inline-flex items-center gap-1 ml-1'>
 
-                     ·
 
-                     <div
 
-                       onClick={() => { setShowDocTypes(true) }}
 
-                       className='cursor-pointer hover:text-[#155EEF]'
 
-                     >
 
-                       {t('common.operation.change')}
 
-                     </div>
 
-                   </div>}
 
-                 </>}
 
-               </div>
 
-           }
 
-           {(!doc_type && showDocTypes) ? null : <Divider />}
 
-           {showDocTypes ? renderSelectDocType() : renderFieldInfos({ mainField: metadataParams.documentType, canEdit: editStatus })}
 
-           {/* show fixed fields */}
 
-           <Divider />
 
-           {renderFieldInfos({ mainField: 'originInfo', canEdit: false })}
 
-           <div className={`${s.title} mt-8`}>{metadataMap.technicalParameters.text}</div>
 
-           <Divider />
 
-           {renderFieldInfos({ mainField: 'technicalParameters', canEdit: false })}
 
-         </>
 
-       )}
 
-     </div>
 
-   )
 
- }
 
- export default Metadata
 
 
  |