wwf
3 天以前 a430284aa21e3ae1f0d5654e55b2ad2852519cc2
app/components/datasets/documents/index.tsx
@@ -1,12 +1,13 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { useDebounce, useDebounceFn } from 'ahooks'
import { groupBy } from 'lodash-es'
import { groupBy, omit } from 'lodash-es'
import { PlusIcon } from '@heroicons/react/24/solid'
import { RiDraftLine, RiExternalLinkLine } from '@remixicon/react'
import { RiExternalLinkLine } from '@remixicon/react'
import AutoDisabledDocument from '../common/document-status-with-action/auto-disabled-document'
import List from './list'
import s from './style.module.css'
@@ -14,23 +15,18 @@
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import { get } from '@/service/base'
import { createDocument } from '@/service/datasets'
import { createDocument, fetchDocuments } from '@/service/datasets'
import { useDatasetDetailContext } from '@/context/dataset-detail'
import { NotionPageSelectorModal } from '@/app/components/base/notion-page-selector'
import type { NotionPage } from '@/models/common'
import type { CreateDocumentReq } from '@/models/datasets'
import { DataSourceType, ProcessMode } from '@/models/datasets'
import { DataSourceType } from '@/models/datasets'
import IndexFailed from '@/app/components/datasets/common/document-status-with-action/index-failed'
import { useProviderContext } from '@/context/provider-context'
import cn from '@/utils/classnames'
import { useDocumentList, useInvalidDocumentDetailKey, useInvalidDocumentList } from '@/service/knowledge/use-document'
import { useInvalidDocumentDetailKey } from '@/service/knowledge/use-document'
import { useInvalid } from '@/service/use-base'
import { useChildSegmentListKey, useSegmentListKey } from '@/service/knowledge/use-segment'
import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata'
import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer'
import StatusWithAction from '../common/document-status-with-action/status-with-action'
import { LanguagesSupported } from '@/i18n/language'
import { getLocaleOnClient } from '@/i18n'
const FolderPlusIcon = ({ className }: React.SVGProps<SVGElement>) => {
  return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
@@ -66,7 +62,7 @@
      <div className={s.emptySymbolIconWrapper}>
        {type === 'upload' ? <FolderPlusIcon /> : <NotionIcon />}
      </div>
      <span className={s.emptyTitle}>{t('datasetDocuments.list.empty.title')}<ThreeDotsIcon className='relative -left-1.5 -top-3 inline' /></span>
      <span className={s.emptyTitle}>{t('datasetDocuments.list.empty.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
      <div className={s.emptyTip}>
        {t(`datasetDocuments.list.empty.${type}.tip`)}
      </div>
@@ -82,7 +78,7 @@
}
export const fetcher = (url: string) => get(url, {}, {})
const DEFAULT_LIMIT = 10
const DEFAULT_LIMIT = 15
const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
  const { t } = useTranslation()
@@ -100,36 +96,36 @@
  const isDataSourceWeb = dataset?.data_source_type === DataSourceType.WEB
  const isDataSourceFile = dataset?.data_source_type === DataSourceType.FILE
  const embeddingAvailable = !!dataset?.embedding_available
  const locale = getLocaleOnClient()
  const debouncedSearchValue = useDebounce(searchValue, { wait: 500 })
  const { data: documentsRes, isFetching: isListLoading } = useDocumentList({
    datasetId,
    query: {
      page: currPage + 1,
      limit,
      keyword: debouncedSearchValue,
  const query = useMemo(() => {
    return { page: currPage + 1, limit, keyword: debouncedSearchValue, fetch: isDataSourceNotion ? true : '' }
  }, [currPage, debouncedSearchValue, isDataSourceNotion, limit])
  const { data: documentsRes, mutate, isLoading: isListLoading } = useSWR(
    {
      action: 'fetchDocuments',
      datasetId,
      params: query,
    },
    refetchInterval: (isDataSourceNotion && timerCanRun) ? 2500 : 0,
  })
    apiParams => fetchDocuments(omit(apiParams, 'action')),
    { refreshInterval: (isDataSourceNotion && timerCanRun) ? 2500 : 0 },
  )
  const invalidDocumentList = useInvalidDocumentList(datasetId)
  const [isMuting, setIsMuting] = useState(false)
  useEffect(() => {
    if (documentsRes) {
      const totalPages = Math.ceil(documentsRes.total / limit)
      if (totalPages < currPage + 1)
        setCurrPage(totalPages === 0 ? 0 : totalPages - 1)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [documentsRes])
    if (!isListLoading && isMuting)
      setIsMuting(false)
  }, [isListLoading, isMuting])
  const invalidDocumentDetail = useInvalidDocumentDetailKey()
  const invalidChunkList = useInvalid(useSegmentListKey)
  const invalidChildChunkList = useInvalid(useChildSegmentListKey)
  const handleUpdate = useCallback(() => {
    invalidDocumentList()
    setIsMuting(true)
    mutate()
    invalidDocumentDetail()
    setTimeout(() => {
      invalidChunkList()
@@ -179,6 +175,8 @@
    router.push(`/datasets/${datasetId}/documents/create`)
  }
  const isLoading = isListLoading // !documentsRes && !error
  const handleSaveNotionPageSelected = async (selectedPages: NotionPage[]) => {
    const workspacesMap = groupBy(selectedPages, 'workspace_id')
    const workspaces = Object.keys(workspacesMap).map((workspaceId) => {
@@ -211,7 +209,7 @@
      indexing_technique: dataset?.indexing_technique,
      process_rule: {
        rules: {},
        mode: ProcessMode.general,
        mode: 'automatic',
      },
    } as CreateDocumentReq
@@ -219,7 +217,7 @@
      datasetId,
      body: params,
    })
    invalidDocumentList()
    mutate()
    setTimerCanRun(true)
    // mutateDatasetIndexingStatus(undefined, { revalidate: true })
    setNotionPageSelectorModalVisible(false)
@@ -236,45 +234,23 @@
    handleSearch()
  }
  const {
    isShowEditModal: isShowEditMetadataModal,
    showEditModal: showEditMetadataModal,
    hideEditModal: hideEditMetadataModal,
    datasetMetaData,
    handleAddMetaData,
    handleRename,
    handleDeleteMetaData,
    builtInEnabled,
    setBuiltInEnabled,
    builtInMetaData,
  } = useEditDocumentMetadata({
    datasetId,
    dataset,
    onUpdateDocList: invalidDocumentList,
  })
  return (
    <div className='flex h-full flex-col overflow-y-auto'>
    <div className='flex flex-col h-full overflow-y-auto'>
      <div className='flex flex-col justify-center gap-1 px-6 pt-4'>
        <h1 className='text-base font-semibold text-text-primary'>{t('datasetDocuments.list.title')}</h1>
        <div className='flex items-center space-x-0.5 text-sm font-normal text-text-tertiary'>
        <div className='flex items-center text-sm font-normal text-text-tertiary space-x-0.5'>
          <span>{t('datasetDocuments.list.desc')}</span>
          <a
            className='flex items-center text-text-accent'
            target='_blank'
            href={
              locale === LanguagesSupported[1]
                ? 'https://docs.dify.ai/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
                : 'https://docs.dify.ai/en/guides/knowledge-base/integrate-knowledge-within-application'
            }
            >
            href='https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'>
            <span>{t('datasetDocuments.list.learnMore')}</span>
            <RiExternalLinkLine className='h-3 w-3' />
            <RiExternalLinkLine className='w-3 h-3' />
          </a>
        </div>
      </div>
      <div className='flex flex-1 flex-col px-6 py-4'>
        <div className='flex flex-wrap items-center justify-between'>
      <div className='flex flex-col px-6 py-4 flex-1'>
        <div className='flex items-center justify-between flex-wrap'>
          <Input
            showLeftIcon
            showClearIcon
@@ -283,31 +259,12 @@
            onChange={e => handleInputChange(e.target.value)}
            onClear={() => handleInputChange('')}
          />
          <div className='flex !h-8 items-center justify-center gap-2'>
          <div className='flex gap-2 justify-center items-center !h-8'>
            {!isFreePlan && <AutoDisabledDocument datasetId={datasetId} />}
            <IndexFailed datasetId={datasetId} />
            {!embeddingAvailable && <StatusWithAction type='warning' description={t('dataset.embeddingModelNotAvailable')} />}
            {embeddingAvailable && (
              <Button variant='secondary' className='shrink-0' onClick={showEditMetadataModal}>
                <RiDraftLine className='mr-1 size-4' />
                {t('dataset.metadata.metadata')}
              </Button>
            )}
            {isShowEditMetadataModal && (
              <DatasetMetadataDrawer
                userMetadata={datasetMetaData || []}
                onClose={hideEditMetadataModal}
                onAdd={handleAddMetaData}
                onRename={handleRename}
                onRemove={handleDeleteMetaData}
                builtInMetadata={builtInMetaData || []}
                isBuiltInEnabled={!!builtInEnabled}
                onIsBuiltInEnabledChange={setBuiltInEnabled}
              />
            )}
            {embeddingAvailable && (
              <Button variant='primary' onClick={routeToDocCreate} className='shrink-0'>
                <PlusIcon className={cn('mr-2 h-4 w-4 stroke-current')} />
                <PlusIcon className={cn('h-4 w-4 mr-2 stroke-current')} />
                {isDataSourceNotion && t('datasetDocuments.list.addPages')}
                {isDataSourceWeb && t('datasetDocuments.list.addUrl')}
                {(!dataset?.data_source_type || isDataSourceFile) && t('datasetDocuments.list.addFile')}
@@ -315,7 +272,7 @@
            )}
          </div>
        </div>
        {isListLoading
        {(isLoading && !isMuting)
          ? <Loading type='app' />
          : total > 0
            ? <List
@@ -332,7 +289,6 @@
                current: currPage,
                onChange: setCurrPage,
              }}
              onManageMetadata={showEditMetadataModal}
            />
            : <EmptyElement canAdd={embeddingAvailable} onClick={routeToDocCreate} type={isDataSourceNotion ? 'sync' : 'upload'} />
        }