dify/web/app/components/datasets/documents/detail/completed/index.tsx

266 lines
9.8 KiB
TypeScript

'use client'
import type { FC } from 'react'
import type { ProcessStatus } from '../segment-add'
import type { SegmentListContextValue } from './segment-list-context'
import { useCallback, useMemo, useState } from 'react'
import Divider from '@/app/components/base/divider'
import Pagination from '@/app/components/base/pagination'
import {
useChunkListAllKey,
useChunkListDisabledKey,
useChunkListEnabledKey,
} from '@/service/knowledge/use-segment'
import { useInvalid } from '@/service/use-base'
import { useDocumentContext } from '../context'
import BatchAction from './common/batch-action'
import { DrawerGroup, FullDocModeContent, GeneralModeContent, MenuBar } from './components'
import {
useChildSegmentData,
useModalState,
useSearchFilter,
useSegmentListData,
useSegmentSelection,
} from './hooks'
import {
SegmentListContext,
useSegmentListContext,
} from './segment-list-context'
const DEFAULT_LIMIT = 10
type ICompletedProps = {
embeddingAvailable: boolean
showNewSegmentModal: boolean
onNewSegmentModalChange: (state: boolean) => void
importStatus: ProcessStatus | string | undefined
archived?: boolean
}
/**
* Embedding done, show list of all segments
* Support search and filter
*/
const Completed: FC<ICompletedProps> = ({
embeddingAvailable,
showNewSegmentModal,
onNewSegmentModalChange,
importStatus,
archived,
}) => {
const docForm = useDocumentContext(s => s.docForm)
// Pagination state
const [currentPage, setCurrentPage] = useState(1)
const [limit, setLimit] = useState(DEFAULT_LIMIT)
// Search and filter state
const searchFilter = useSearchFilter({
onPageChange: setCurrentPage,
})
// Modal state
const modalState = useModalState({
onNewSegmentModalChange,
})
// Selection state (need segments first, so we use a placeholder initially)
const [segmentsForSelection, setSegmentsForSelection] = useState<string[]>([])
// Invalidation hooks for child segment data
const invalidChunkListAll = useInvalid(useChunkListAllKey)
const invalidChunkListEnabled = useInvalid(useChunkListEnabledKey)
const invalidChunkListDisabled = useInvalid(useChunkListDisabledKey)
const refreshChunkListDataWithDetailChanged = useCallback(() => {
const refreshMap: Record<string, () => void> = {
all: () => {
invalidChunkListDisabled()
invalidChunkListEnabled()
},
true: () => {
invalidChunkListAll()
invalidChunkListDisabled()
},
false: () => {
invalidChunkListAll()
invalidChunkListEnabled()
},
}
refreshMap[String(searchFilter.selectedStatus)]?.()
}, [searchFilter.selectedStatus, invalidChunkListDisabled, invalidChunkListEnabled, invalidChunkListAll])
// Segment list data
const segmentListDataHook = useSegmentListData({
searchValue: searchFilter.searchValue,
selectedStatus: searchFilter.selectedStatus,
selectedSegmentIds: segmentsForSelection,
importStatus,
currentPage,
limit,
onCloseSegmentDetail: modalState.onCloseSegmentDetail,
clearSelection: () => setSegmentsForSelection([]),
})
// Selection state (with actual segments)
const selectionState = useSegmentSelection(segmentListDataHook.segments)
// Sync selection state for segment list data hook
useMemo(() => {
setSegmentsForSelection(selectionState.selectedSegmentIds)
}, [selectionState.selectedSegmentIds])
// Child segment data
const childSegmentDataHook = useChildSegmentData({
searchValue: searchFilter.searchValue,
currentPage,
limit,
segments: segmentListDataHook.segments,
currChunkId: modalState.currChunkId,
isFullDocMode: segmentListDataHook.isFullDocMode,
onCloseChildSegmentDetail: modalState.onCloseChildSegmentDetail,
refreshChunkListDataWithDetailChanged,
updateSegmentInCache: segmentListDataHook.updateSegmentInCache,
})
// Compute total for pagination
const paginationTotal = useMemo(() => {
if (segmentListDataHook.isFullDocMode)
return childSegmentDataHook.childChunkListData?.total || 0
return segmentListDataHook.segmentListData?.total || 0
}, [segmentListDataHook.isFullDocMode, childSegmentDataHook.childChunkListData, segmentListDataHook.segmentListData])
// Handle page change
const handlePageChange = useCallback((page: number) => {
setCurrentPage(page + 1)
}, [])
// Context value
const contextValue = useMemo<SegmentListContextValue>(() => ({
isCollapsed: modalState.isCollapsed,
fullScreen: modalState.fullScreen,
toggleFullScreen: modalState.toggleFullScreen,
currSegment: modalState.currSegment,
currChildChunk: modalState.currChildChunk,
}), [
modalState.isCollapsed,
modalState.fullScreen,
modalState.toggleFullScreen,
modalState.currSegment,
modalState.currChildChunk,
])
return (
<SegmentListContext.Provider value={contextValue}>
{/* Menu Bar */}
{!segmentListDataHook.isFullDocMode && (
<MenuBar
isAllSelected={selectionState.isAllSelected}
isSomeSelected={selectionState.isSomeSelected}
onSelectedAll={selectionState.onSelectedAll}
isLoading={segmentListDataHook.isLoadingSegmentList}
totalText={segmentListDataHook.totalText}
statusList={searchFilter.statusList}
selectDefaultValue={searchFilter.selectDefaultValue}
onChangeStatus={searchFilter.onChangeStatus}
inputValue={searchFilter.inputValue}
onInputChange={searchFilter.handleInputChange}
isCollapsed={modalState.isCollapsed}
toggleCollapsed={modalState.toggleCollapsed}
/>
)}
{/* Segment list */}
{segmentListDataHook.isFullDocMode
? (
<FullDocModeContent
segments={segmentListDataHook.segments}
childSegments={childSegmentDataHook.childSegments}
isLoadingSegmentList={segmentListDataHook.isLoadingSegmentList}
isLoadingChildSegmentList={childSegmentDataHook.isLoadingChildSegmentList}
currSegmentId={modalState.currSegment?.segInfo?.id}
onClickCard={modalState.onClickCard}
onDeleteChildChunk={childSegmentDataHook.onDeleteChildChunk}
handleInputChange={searchFilter.handleInputChange}
handleAddNewChildChunk={modalState.handleAddNewChildChunk}
onClickSlice={modalState.onClickSlice}
archived={archived}
childChunkTotal={childSegmentDataHook.childChunkListData?.total || 0}
inputValue={searchFilter.inputValue}
onClearFilter={searchFilter.onClearFilter}
/>
)
: (
<GeneralModeContent
segmentListRef={segmentListDataHook.segmentListRef}
embeddingAvailable={embeddingAvailable}
isLoadingSegmentList={segmentListDataHook.isLoadingSegmentList}
segments={segmentListDataHook.segments}
selectedSegmentIds={selectionState.selectedSegmentIds}
onSelected={selectionState.onSelected}
onChangeSwitch={segmentListDataHook.onChangeSwitch}
onDelete={segmentListDataHook.onDelete}
onClickCard={modalState.onClickCard}
archived={archived}
onDeleteChildChunk={childSegmentDataHook.onDeleteChildChunk}
handleAddNewChildChunk={modalState.handleAddNewChildChunk}
onClickSlice={modalState.onClickSlice}
onClearFilter={searchFilter.onClearFilter}
/>
)}
{/* Pagination */}
<Divider type="horizontal" className="mx-6 my-0 h-px w-auto bg-divider-subtle" />
<Pagination
current={currentPage - 1}
onChange={handlePageChange}
total={paginationTotal}
limit={limit}
onLimitChange={setLimit}
className={segmentListDataHook.isFullDocMode ? 'px-3' : ''}
/>
{/* Drawer Group - only render when docForm is available */}
{docForm && (
<DrawerGroup
currSegment={modalState.currSegment}
onCloseSegmentDetail={modalState.onCloseSegmentDetail}
onUpdateSegment={segmentListDataHook.handleUpdateSegment}
isRegenerationModalOpen={modalState.isRegenerationModalOpen}
setIsRegenerationModalOpen={modalState.setIsRegenerationModalOpen}
showNewSegmentModal={showNewSegmentModal}
onCloseNewSegmentModal={modalState.onCloseNewSegmentModal}
onSaveNewSegment={segmentListDataHook.resetList}
viewNewlyAddedChunk={segmentListDataHook.viewNewlyAddedChunk}
currChildChunk={modalState.currChildChunk}
currChunkId={modalState.currChunkId}
onCloseChildSegmentDetail={modalState.onCloseChildSegmentDetail}
onUpdateChildChunk={childSegmentDataHook.handleUpdateChildChunk}
showNewChildSegmentModal={modalState.showNewChildSegmentModal}
onCloseNewChildChunkModal={modalState.onCloseNewChildChunkModal}
onSaveNewChildChunk={childSegmentDataHook.onSaveNewChildChunk}
viewNewlyAddedChildChunk={childSegmentDataHook.viewNewlyAddedChildChunk}
fullScreen={modalState.fullScreen}
docForm={docForm}
/>
)}
{/* Batch Action Buttons */}
{selectionState.selectedSegmentIds.length > 0 && (
<BatchAction
className="absolute bottom-16 left-0 z-20"
selectedIds={selectionState.selectedSegmentIds}
onBatchEnable={() => segmentListDataHook.onChangeSwitch(true, '')}
onBatchDisable={() => segmentListDataHook.onChangeSwitch(false, '')}
onBatchDelete={() => segmentListDataHook.onDelete('')}
onCancel={selectionState.onCancelBatchOperation}
/>
)}
</SegmentListContext.Provider>
)
}
export { useSegmentListContext }
export type { SegmentListContextValue }
export default Completed