Skip to content

Commit

Permalink
feat: document support rename in in dataset (langgenius#4732)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamjoel authored Jun 4, 2024
1 parent 9cf9720 commit 96460d5
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 17 deletions.
1 change: 1 addition & 0 deletions web/app/components/datasets/documents/detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
scene='detail'
embeddingAvailable={embeddingAvailable}
detail={{
name: documentDetail?.name || '',
enabled: documentDetail?.enabled || false,
archived: documentDetail?.archived || false,
id: documentId,
Expand Down
120 changes: 103 additions & 17 deletions web/app/components/datasets/documents/list.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable no-mixed-operators */
'use client'
import type { FC, SVGProps } from 'react'
import React, { useEffect, useState } from 'react'
import { useDebounceFn } from 'ahooks'
import React, { useCallback, useEffect, useState } from 'react'
import { useBoolean, useDebounceFn } from 'ahooks'
import { ArrowDownIcon, TrashIcon } from '@heroicons/react/24/outline'
import { ExclamationCircleIcon } from '@heroicons/react/24/solid'
import { pick } from 'lodash-es'
Expand All @@ -11,7 +11,10 @@ import { useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import dayjs from 'dayjs'
import { Edit03 } from '../../base/icons/src/vender/solid/general'
import TooltipPlus from '../../base/tooltip-plus'
import s from './style.module.css'
import RenameModal from './rename-modal'
import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider'
import Popover from '@/app/components/base/popover'
Expand Down Expand Up @@ -39,7 +42,7 @@ export const SettingsIcon = ({ className }: SVGProps<SVGElement>) => {

export const SyncIcon = () => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.69773 13.1783C7.29715 13.8879 9.20212 13.8494 10.8334 12.9075C13.5438 11.3427 14.4724 7.87704 12.9076 5.16672L12.7409 4.87804M3.09233 10.8335C1.52752 8.12314 2.45615 4.65746 5.16647 3.09265C6.7978 2.15081 8.70277 2.11227 10.3022 2.82185M1.66226 10.8892L3.48363 11.3773L3.97166 9.5559M12.0284 6.44393L12.5164 4.62256L14.3378 5.1106" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.69773 13.1783C7.29715 13.8879 9.20212 13.8494 10.8334 12.9075C13.5438 11.3427 14.4724 7.87704 12.9076 5.16672L12.7409 4.87804M3.09233 10.8335C1.52752 8.12314 2.45615 4.65746 5.16647 3.09265C6.7978 2.15081 8.70277 2.11227 10.3022 2.82185M1.66226 10.8892L3.48363 11.3773L3.97166 9.5559M12.0284 6.44393L12.5164 4.62256L14.3378 5.1106" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}

Expand Down Expand Up @@ -107,6 +110,7 @@ type OperationName = 'delete' | 'archive' | 'enable' | 'disable' | 'sync' | 'un_
export const OperationAction: FC<{
embeddingAvailable: boolean
detail: {
name: string
enabled: boolean
archived: boolean
id: string
Expand Down Expand Up @@ -164,6 +168,25 @@ export const OperationAction: FC<{
onOperate(operationName)
}, { wait: 500 })

const [currDocument, setCurrDocument] = useState<{
id: string
name: string
} | null>(null)
const [isShowRenameModal, {
setTrue: setShowRenameModalTrue,
setFalse: setShowRenameModalFalse,
}] = useBoolean(false)
const handleShowRenameModal = useCallback((doc: {
id: string
name: string
}) => {
setCurrDocument(doc)
setShowRenameModalTrue()
}, [setShowRenameModalTrue])
const handleRenamed = useCallback(() => {
onUpdate()
}, [onUpdate])

return <div className='flex items-center' onClick={e => e.stopPropagation()}>
{isListScene && !embeddingAvailable && (
<Switch defaultValue={false} onChange={() => { }} disabled={true} size='md' />
Expand Down Expand Up @@ -213,6 +236,15 @@ export const OperationAction: FC<{
</>}
{!archived && (
<>
<div className={s.actionItem} onClick={() => {
handleShowRenameModal({
id: detail.id,
name: detail.name,
})
}}>
<Edit03 className='w-4 h-4 text-gray-500' />
<span className={s.actionName}>{t('datasetDocuments.list.table.rename')}</span>
</div>
<div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}>
<SettingsIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.settings')}</span>
Expand Down Expand Up @@ -272,6 +304,16 @@ export const OperationAction: FC<{
</div>
</div>
</Modal>}

{isShowRenameModal && currDocument && (
<RenameModal
datasetId={datasetId}
documentId={currDocument.id}
name={currDocument.name}
onClose={setShowRenameModalFalse}
onSaved={handleRenamed}
/>
)}
</div>
}

Expand Down Expand Up @@ -326,13 +368,30 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
}
}

const [currDocument, setCurrDocument] = useState<LocalDoc | null>(null)
const [isShowRenameModal, {
setTrue: setShowRenameModalTrue,
setFalse: setShowRenameModalFalse,
}] = useBoolean(false)
const handleShowRenameModal = useCallback((doc: LocalDoc) => {
setCurrDocument(doc)
setShowRenameModalTrue()
}, [setShowRenameModalTrue])
const handleRenamed = useCallback(() => {
onUpdate()
}, [onUpdate])

return (
<div className='w-full h-full overflow-x-auto'>
<table className={`min-w-[700px] max-w-full w-full border-collapse border-0 text-sm mt-3 ${s.documentTable}`}>
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-medium text-xs uppercase">
<tr>
<td className='w-12'>#</td>
<td>{t('datasetDocuments.list.table.header.fileName')}</td>
<td>
<div className='flex'>
{t('datasetDocuments.list.table.header.fileName')}
</div>
</td>
<td className='w-24'>{t('datasetDocuments.list.table.header.words')}</td>
<td className='w-44'>{t('datasetDocuments.list.table.header.hitCount')}</td>
<td className='w-44'>
Expand All @@ -347,25 +406,42 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
</thead>
<tbody className="text-gray-700">
{localDocs.map((doc) => {
const suffix = doc.name.split('.').pop() || 'txt'
const isFile = doc.data_source_type === DataSourceType.FILE
const fileType = isFile ? doc.data_source_detail_dict?.upload_file.extension : ''
return <tr
key={doc.id}
className={'border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer'}
onClick={() => {
router.push(`/datasets/${datasetId}/documents/${doc.id}`)
}}>
<td className='text-left align-middle text-gray-500 text-xs'>{doc.position}</td>
<td className={s.tdValue}>
{
doc?.data_source_type === DataSourceType.NOTION
? <NotionIcon className='inline-flex -mt-[3px] mr-1.5 align-middle' type='page' src={doc.data_source_info.notion_page_icon} />
: <div className={cn(s[`${doc?.data_source_info?.upload_file?.extension ?? suffix}Icon`], s.commonIcon, 'mr-1.5')}></div>
}
{
doc.data_source_type === DataSourceType.NOTION
? <span>{doc.name}</span>
: <span>{doc?.name?.replace(/\.[^/.]+$/, '')}<span className='text-gray-500'>.{suffix}</span></span>
}
<td>
<div className='group flex items-center justify-between'>
<span className={s.tdValue}>
{
doc?.data_source_type === DataSourceType.NOTION
? <NotionIcon className='inline-flex -mt-[3px] mr-1.5 align-middle' type='page' src={doc.data_source_info.notion_page_icon} />
: <div className={cn(s[`${doc?.data_source_info?.upload_file?.extension ?? fileType}Icon`], s.commonIcon, 'mr-1.5')}></div>
}
{
doc.name
}
</span>
<div className='group-hover:flex hidden'>
<TooltipPlus popupContent={t('datasetDocuments.list.table.rename')}>
<div
className='p-1 rounded-md cursor-pointer hover:bg-black/5'
onClick={(e) => {
e.stopPropagation()
handleShowRenameModal(doc)
}}
>
<Edit03 className='w-4 h-4 text-gray-500' />
</div>
</TooltipPlus>
</div>
</div>

</td>
<td>{renderCount(doc.word_count)}</td>
<td>{renderCount(doc.hit_count)}</td>
Expand All @@ -383,14 +459,24 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
<OperationAction
embeddingAvailable={embeddingAvailable}
datasetId={datasetId}
detail={pick(doc, ['enabled', 'archived', 'id', 'data_source_type', 'doc_form'])}
detail={pick(doc, ['name', 'enabled', 'archived', 'id', 'data_source_type', 'doc_form'])}
onUpdate={onUpdate}
/>
</td>
</tr>
})}
</tbody>
</table>

{isShowRenameModal && currDocument && (
<RenameModal
datasetId={datasetId}
documentId={currDocument.id}
name={currDocument.name}
onClose={setShowRenameModalFalse}
onSaved={handleRenamed}
/>
)}
</div>
)
}
Expand Down
75 changes: 75 additions & 0 deletions web/app/components/datasets/documents/rename-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import Toast from '../../base/toast'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import { renameDocumentName } from '@/service/datasets'

type Props = {
datasetId: string
documentId: string
name: string
onClose: () => void
onSaved: () => void
}

const RenameModal: FC<Props> = ({
documentId,
datasetId,
name,
onClose,
onSaved,
}) => {
const { t } = useTranslation()

const [newName, setNewName] = useState(name)
const [saveLoading, {
setTrue: setSaveLoadingTrue,
setFalse: setSaveLoadingFalse,
}] = useBoolean(false)

const handleSave = async () => {
setSaveLoadingTrue()
try {
await renameDocumentName({
datasetId,
documentId,
name: newName,
})
Toast.notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
onSaved()
onClose()
}
catch (error) {
if (error)
Toast.notify({ type: 'error', message: error.toString() })
}
finally {
setSaveLoadingFalse()
}
}

return (
<Modal
title={t('datasetDocuments.list.table.rename')}
isShow
onClose={onClose}
wrapperClassName='!z-50'
>
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('datasetDocuments.list.table.name')}</div>
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
value={newName}
onChange={e => setNewName(e.target.value)}
/>

<div className='mt-10 flex justify-end'>
<Button className='mr-2 flex-shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button type='primary' className='flex-shrink-0' onClick={handleSave} loading={saveLoading}>{t('common.operation.save')}</Button>
</div>
</Modal>
)
}
export default React.memo(RenameModal)
2 changes: 2 additions & 0 deletions web/i18n/en-US/dataset-documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const translation = {
status: 'STATUS',
action: 'ACTION',
},
rename: 'Rename',
name: 'Name',
},
action: {
uploadFile: 'Upload new file',
Expand Down
2 changes: 2 additions & 0 deletions web/i18n/zh-Hans/dataset-documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const translation = {
status: '状态',
action: '操作',
},
rename: '重命名',
name: '名称',
},
action: {
uploadFile: '上传新文件',
Expand Down
6 changes: 6 additions & 0 deletions web/models/datasets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ export type SimpleDocumentDetail = InitialDocumentDetail & {
updated_at: number
hit_count: number
dataset_process_rule_id?: string
data_source_detail_dict?: {
upload_file: {
name: string
extension: string
}
}
}

export type DocumentListResponse = {
Expand Down
6 changes: 6 additions & 0 deletions web/service/datasets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ export const fetchDocumentDetail: Fetcher<DocumentDetailResponse, CommonDocReq &
return get<DocumentDetailResponse>(`/datasets/${datasetId}/documents/${documentId}`, { params })
}

export const renameDocumentName: Fetcher<CommonResponse, CommonDocReq & { name: string }> = ({ datasetId, documentId, name }) => {
return post<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/rename`, {
body: { name },
})
}

export const pauseDocIndexing: Fetcher<CommonResponse, CommonDocReq> = ({ datasetId, documentId }) => {
return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/processing/pause`)
}
Expand Down

0 comments on commit 96460d5

Please sign in to comment.