| import { useCallback, useEffect, useMemo, useState } from 'react' | import { useCallback, useEffect, useMemo, useState } from 'react' | ||||
| import { useTranslation } from 'react-i18next' | |||||
| import { useContext } from 'use-context-selector' | |||||
| import useSWR from 'swr' | import useSWR from 'swr' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import s from './base.module.css' | import s from './base.module.css' | ||||
| import AccountSetting from '@/app/components/header/account-setting' | import AccountSetting from '@/app/components/header/account-setting' | ||||
| import { NotionConnector } from '@/app/components/datasets/create/step-one' | import { NotionConnector } from '@/app/components/datasets/create/step-one' | ||||
| import type { DataSourceNotionPage, DataSourceNotionPageMap, DataSourceNotionWorkspace } from '@/models/common' | import type { DataSourceNotionPage, DataSourceNotionPageMap, DataSourceNotionWorkspace } from '@/models/common' | ||||
| import { ToastContext } from '@/app/components/base/toast' | |||||
| export type NotionPageSelectorValue = DataSourceNotionPage & { workspace_id: string } | export type NotionPageSelectorValue = DataSourceNotionPage & { workspace_id: string } | ||||
| previewPageId?: string | previewPageId?: string | ||||
| onPreview?: (selectedPage: NotionPageSelectorValue) => void | onPreview?: (selectedPage: NotionPageSelectorValue) => void | ||||
| datasetId?: string | datasetId?: string | ||||
| countLimit: number | |||||
| countUsed: number | |||||
| } | } | ||||
| const NotionPageSelector = ({ | const NotionPageSelector = ({ | ||||
| previewPageId, | previewPageId, | ||||
| onPreview, | onPreview, | ||||
| datasetId = '', | datasetId = '', | ||||
| countLimit, | |||||
| countUsed, | |||||
| }: NotionPageSelectorProps) => { | }: NotionPageSelectorProps) => { | ||||
| const { t } = useTranslation() | |||||
| const { notify } = useContext(ToastContext) | |||||
| const { data, mutate } = useSWR({ url: '/notion/pre-import/pages', datasetId }, preImportNotionPages) | const { data, mutate } = useSWR({ url: '/notion/pre-import/pages', datasetId }, preImportNotionPages) | ||||
| const [prevData, setPrevData] = useState(data) | const [prevData, setPrevData] = useState(data) | ||||
| const [searchValue, setSearchValue] = useState('') | const [searchValue, setSearchValue] = useState('') | ||||
| const handleSelectWorkspace = useCallback((workspaceId: string) => { | const handleSelectWorkspace = useCallback((workspaceId: string) => { | ||||
| setCurrentWorkspaceId(workspaceId) | setCurrentWorkspaceId(workspaceId) | ||||
| }, []) | }, []) | ||||
| const handleSelecPages = (selectedPagesId: Set<string>) => { | |||||
| setSelectedPagesId(new Set(Array.from(selectedPagesId))) | |||||
| const selectedPages = Array.from(selectedPagesId).map(pageId => getPagesMapAndSelectedPagesId[0][pageId]) | |||||
| const handleSelecPages = (newSelectedPagesId: Set<string>) => { | |||||
| const selectedPages = Array.from(newSelectedPagesId).map(pageId => getPagesMapAndSelectedPagesId[0][pageId]) | |||||
| if (selectedPages.length > countLimit - countUsed) { | |||||
| notify({ type: 'error', message: t('datasetCreation.stepOne.overCountLimit', { countLimit }) }) | |||||
| return false | |||||
| } | |||||
| setSelectedPagesId(new Set(Array.from(newSelectedPagesId))) | |||||
| onSelect(selectedPages) | onSelect(selectedPages) | ||||
| } | } | ||||
| const handlePreviewPage = (previewPageId: string) => { | const handlePreviewPage = (previewPageId: string) => { |
| setDataList(newDataList) | setDataList(newDataList) | ||||
| } | } | ||||
| const copyValue = new Set([...value]) | |||||
| const handleCheck = (index: number) => { | const handleCheck = (index: number) => { | ||||
| const current = currentDataList[index] | const current = currentDataList[index] | ||||
| const pageId = current.page_id | const pageId = current.page_id | ||||
| const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId] | const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId] | ||||
| if (value.has(pageId)) { | |||||
| if (copyValue.has(pageId)) { | |||||
| if (!searchValue) { | if (!searchValue) { | ||||
| for (const item of currentWithChildrenAndDescendants.descendants) | for (const item of currentWithChildrenAndDescendants.descendants) | ||||
| value.delete(item) | |||||
| copyValue.delete(item) | |||||
| } | } | ||||
| value.delete(pageId) | |||||
| copyValue.delete(pageId) | |||||
| } | } | ||||
| else { | else { | ||||
| if (!searchValue) { | if (!searchValue) { | ||||
| for (const item of currentWithChildrenAndDescendants.descendants) | for (const item of currentWithChildrenAndDescendants.descendants) | ||||
| value.add(item) | |||||
| copyValue.add(item) | |||||
| } | } | ||||
| value.add(pageId) | |||||
| copyValue.add(pageId) | |||||
| } | } | ||||
| onSelect(new Set([...value])) | |||||
| onSelect(new Set([...copyValue])) | |||||
| } | } | ||||
| const handlePreview = (index: number) => { | const handlePreview = (index: number) => { |
| onFileUpdate: (fileItem: FileItem, progress: number, list: FileItem[]) => void | onFileUpdate: (fileItem: FileItem, progress: number, list: FileItem[]) => void | ||||
| onFileListUpdate?: (files: any) => void | onFileListUpdate?: (files: any) => void | ||||
| onPreview: (file: File) => void | onPreview: (file: File) => void | ||||
| countLimit: number | |||||
| countUsed: number | |||||
| } | } | ||||
| const ACCEPTS = [ | const ACCEPTS = [ | ||||
| onFileUpdate, | onFileUpdate, | ||||
| onFileListUpdate, | onFileListUpdate, | ||||
| onPreview, | onPreview, | ||||
| countLimit, | |||||
| countUsed, | |||||
| }: IFileUploaderProps) => { | }: IFileUploaderProps) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { notify } = useContext(ToastContext) | const { notify } = useContext(ToastContext) | ||||
| const initialUpload = useCallback((files: File[]) => { | const initialUpload = useCallback((files: File[]) => { | ||||
| if (!files.length) | if (!files.length) | ||||
| return false | return false | ||||
| if (files.length > countLimit - countUsed) { | |||||
| notify({ type: 'error', message: t('datasetCreation.stepOne.overCountLimit', { countLimit }) }) | |||||
| return false | |||||
| } | |||||
| const preparedFiles = files.map((file, index) => ({ | const preparedFiles = files.map((file, index) => ({ | ||||
| fileID: `file${index}-${Date.now()}`, | fileID: `file${index}-${Date.now()}`, | ||||
| file, | file, |
| 'use client' | 'use client' | ||||
| import React, { useMemo, useState } from 'react' | import React, { useMemo, useState } from 'react' | ||||
| import useSWR from 'swr' | |||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import FilePreview from '../file-preview' | import FilePreview from '../file-preview' | ||||
| import Button from '@/app/components/base/button' | import Button from '@/app/components/base/button' | ||||
| import { NotionPageSelector } from '@/app/components/base/notion-page-selector' | import { NotionPageSelector } from '@/app/components/base/notion-page-selector' | ||||
| import { useDatasetDetailContext } from '@/context/dataset-detail' | import { useDatasetDetailContext } from '@/context/dataset-detail' | ||||
| import { fetchDocumentsLimit } from '@/service/common' | |||||
| type IStepOneProps = { | type IStepOneProps = { | ||||
| datasetId?: string | datasetId?: string | ||||
| notionPages = [], | notionPages = [], | ||||
| updateNotionPages, | updateNotionPages, | ||||
| }: IStepOneProps) => { | }: IStepOneProps) => { | ||||
| const { data: limitsData } = useSWR('/datasets/limit', fetchDocumentsLimit) | |||||
| const { dataset } = useDatasetDetailContext() | const { dataset } = useDatasetDetailContext() | ||||
| const [showModal, setShowModal] = useState(false) | const [showModal, setShowModal] = useState(false) | ||||
| const [currentFile, setCurrentFile] = useState<File | undefined>() | const [currentFile, setCurrentFile] = useState<File | undefined>() | ||||
| </div> | </div> | ||||
| ) | ) | ||||
| } | } | ||||
| {dataSourceType === DataSourceType.FILE && ( | |||||
| {dataSourceType === DataSourceType.FILE && limitsData && ( | |||||
| <> | <> | ||||
| <FileUploader | <FileUploader | ||||
| fileList={files} | fileList={files} | ||||
| onFileListUpdate={updateFileList} | onFileListUpdate={updateFileList} | ||||
| onFileUpdate={updateFile} | onFileUpdate={updateFile} | ||||
| onPreview={updateCurrentFile} | onPreview={updateCurrentFile} | ||||
| countLimit={limitsData.documents_limit} | |||||
| countUsed={limitsData.documents_count} | |||||
| /> | /> | ||||
| <Button disabled={nextDisabled} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button> | <Button disabled={nextDisabled} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button> | ||||
| </> | </> | ||||
| {dataSourceType === DataSourceType.NOTION && ( | {dataSourceType === DataSourceType.NOTION && ( | ||||
| <> | <> | ||||
| {!hasConnection && <NotionConnector onSetting={onSetting} />} | {!hasConnection && <NotionConnector onSetting={onSetting} />} | ||||
| {hasConnection && ( | |||||
| {hasConnection && limitsData && ( | |||||
| <> | <> | ||||
| <div className='mb-8 w-[640px]'> | <div className='mb-8 w-[640px]'> | ||||
| <NotionPageSelector value={notionPages.map(page => page.page_id)} onSelect={updateNotionPages} onPreview={updateCurrentPage} /> | |||||
| <NotionPageSelector | |||||
| value={notionPages.map(page => page.page_id)} | |||||
| onSelect={updateNotionPages} | |||||
| onPreview={updateCurrentPage} | |||||
| countLimit={limitsData.documents_limit} | |||||
| countUsed={limitsData.documents_count} | |||||
| /> | |||||
| </div> | </div> | ||||
| <Button disabled={!notionPages.length} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button> | <Button disabled={!notionPages.length} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button> | ||||
| </> | </> |
| confirmButton: 'Create', | confirmButton: 'Create', | ||||
| failed: 'Creation failed', | failed: 'Creation failed', | ||||
| }, | }, | ||||
| overCountLimit: 'All your documents have overed limit {{countLimit}}.', | |||||
| }, | }, | ||||
| stepTwo: { | stepTwo: { | ||||
| segmentation: 'Segmentation settings', | segmentation: 'Segmentation settings', |
| confirmButton: '创建', | confirmButton: '创建', | ||||
| failed: '创建失败', | failed: '创建失败', | ||||
| }, | }, | ||||
| overCountLimit: '您的文件总数已超出限制 {{countLimit}}。', | |||||
| }, | }, | ||||
| stepTwo: { | stepTwo: { | ||||
| segmentation: '分段设置', | segmentation: '分段设置', |
| file_size_limit: number | file_size_limit: number | ||||
| batch_count_limit: number | batch_count_limit: number | ||||
| } | } | ||||
| export type DocumentsLimitResponse = { | |||||
| documents_count: number | |||||
| documents_limit: number | |||||
| } |
| import { del, get, patch, post, put } from './base' | import { del, get, patch, post, put } from './base' | ||||
| import type { | import type { | ||||
| AccountIntegrate, CommonResponse, DataSourceNotion, | AccountIntegrate, CommonResponse, DataSourceNotion, | ||||
| DocumentsLimitResponse, | |||||
| FileUploadConfigResponse, | FileUploadConfigResponse, | ||||
| ICurrentWorkspace, | ICurrentWorkspace, | ||||
| IWorkspace, LangGeniusVersionResponse, Member, | IWorkspace, LangGeniusVersionResponse, Member, | ||||
| export const fetchFileUploadConfig: Fetcher<FileUploadConfigResponse, { url: string }> = ({ url }) => { | export const fetchFileUploadConfig: Fetcher<FileUploadConfigResponse, { url: string }> = ({ url }) => { | ||||
| return get(url) as Promise<FileUploadConfigResponse> | return get(url) as Promise<FileUploadConfigResponse> | ||||
| } | } | ||||
| export const fetchDocumentsLimit: Fetcher<DocumentsLimitResponse, string> = (url) => { | |||||
| return get(url) as Promise<DocumentsLimitResponse> | |||||
| } |