mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 01:48:04 +08:00
chore(web): new lint setup (#30020)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
@ -1,7 +1,11 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type {
|
||||
DataSourceInfo,
|
||||
FullDocumentDetail,
|
||||
IndexingStatusResponse,
|
||||
LegacyDataSourceInfo,
|
||||
ProcessRuleResponse,
|
||||
} from '@/models/datasets'
|
||||
import {
|
||||
RiArrowRightLine,
|
||||
RiCheckboxCircleFill,
|
||||
@ -10,35 +14,31 @@ import {
|
||||
RiTerminalBoxLine,
|
||||
} from '@remixicon/react'
|
||||
import Image from 'next/image'
|
||||
import { indexMethodIcon, retrievalIcon } from '../icons'
|
||||
import { IndexingType } from '../step-two'
|
||||
import DocumentFileIcon from '../../common/document-file-icon'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { FieldInfo } from '@/app/components/datasets/documents/detail/metadata'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import type {
|
||||
DataSourceInfo,
|
||||
FullDocumentDetail,
|
||||
IndexingStatusResponse,
|
||||
LegacyDataSourceInfo,
|
||||
ProcessRuleResponse,
|
||||
} from '@/models/datasets'
|
||||
import { fetchIndexingStatusBatch as doFetchIndexingStatus } from '@/service/datasets'
|
||||
import { DataSourceType, ProcessMode } from '@/models/datasets'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { ZapFast } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import NotionIcon from '@/app/components/base/notion-icon'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import PriorityLabel from '@/app/components/billing/priority-label'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import { ZapFast } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||
import { FieldInfo } from '@/app/components/datasets/documents/detail/metadata'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { sleep } from '@/utils'
|
||||
import { RETRIEVE_METHOD } from '@/types/app'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useInvalidDocumentList } from '@/service/knowledge/use-document'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
|
||||
import Link from 'next/link'
|
||||
import { DataSourceType, ProcessMode } from '@/models/datasets'
|
||||
import { fetchIndexingStatusBatch as doFetchIndexingStatus } from '@/service/datasets'
|
||||
import { useProcessRule } from '@/service/knowledge/use-dataset'
|
||||
import { useInvalidDocumentList } from '@/service/knowledge/use-document'
|
||||
import { RETRIEVE_METHOD } from '@/types/app'
|
||||
import { sleep } from '@/utils'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import DocumentFileIcon from '../../common/document-file-icon'
|
||||
import { indexMethodIcon, retrievalIcon } from '../icons'
|
||||
import { IndexingType } from '../step-two'
|
||||
|
||||
type Props = {
|
||||
datasetId: string
|
||||
@ -105,54 +105,58 @@ const RuleDetail: FC<{
|
||||
value = !sourceData?.mode
|
||||
? value
|
||||
: sourceData?.rules?.pre_processing_rules?.filter(rule =>
|
||||
rule.enabled).map(rule => getRuleName(rule.id)).join(',')
|
||||
rule.enabled).map(rule => getRuleName(rule.id)).join(',')
|
||||
break
|
||||
}
|
||||
return value
|
||||
}, [sourceData])
|
||||
|
||||
return <div className='flex flex-col gap-1'>
|
||||
{Object.keys(segmentationRuleMap).map((field) => {
|
||||
return <FieldInfo
|
||||
key={field}
|
||||
label={segmentationRuleMap[field as keyof typeof segmentationRuleMap]}
|
||||
displayedValue={String(getValue(field))}
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
{Object.keys(segmentationRuleMap).map((field) => {
|
||||
return (
|
||||
<FieldInfo
|
||||
key={field}
|
||||
label={segmentationRuleMap[field as keyof typeof segmentationRuleMap]}
|
||||
displayedValue={String(getValue(field))}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<FieldInfo
|
||||
label={t('datasetCreation.stepTwo.indexMode')}
|
||||
displayedValue={t(`datasetCreation.stepTwo.${indexingType === IndexingType.ECONOMICAL ? 'economical' : 'qualified'}`) as string}
|
||||
valueIcon={(
|
||||
<Image
|
||||
className="size-4"
|
||||
src={
|
||||
indexingType === IndexingType.ECONOMICAL
|
||||
? indexMethodIcon.economical
|
||||
: indexMethodIcon.high_quality
|
||||
}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
})}
|
||||
<FieldInfo
|
||||
label={t('datasetCreation.stepTwo.indexMode')}
|
||||
displayedValue={t(`datasetCreation.stepTwo.${indexingType === IndexingType.ECONOMICAL ? 'economical' : 'qualified'}`) as string}
|
||||
valueIcon={
|
||||
<Image
|
||||
className='size-4'
|
||||
src={
|
||||
indexingType === IndexingType.ECONOMICAL
|
||||
? indexMethodIcon.economical
|
||||
: indexMethodIcon.high_quality
|
||||
}
|
||||
alt=''
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FieldInfo
|
||||
label={t('datasetSettings.form.retrievalSetting.title')}
|
||||
// displayedValue={t(`datasetSettings.form.retrievalSetting.${retrievalMethod}`) as string}
|
||||
displayedValue={t(`dataset.retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod}.title`) as string}
|
||||
valueIcon={
|
||||
<Image
|
||||
className='size-4'
|
||||
src={
|
||||
retrievalMethod === RETRIEVE_METHOD.fullText
|
||||
? retrievalIcon.fullText
|
||||
: retrievalMethod === RETRIEVE_METHOD.hybrid
|
||||
? retrievalIcon.hybrid
|
||||
: retrievalIcon.vector
|
||||
}
|
||||
alt=''
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<FieldInfo
|
||||
label={t('datasetSettings.form.retrievalSetting.title')}
|
||||
// displayedValue={t(`datasetSettings.form.retrievalSetting.${retrievalMethod}`) as string}
|
||||
displayedValue={t(`dataset.retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod}.title`) as string}
|
||||
valueIcon={(
|
||||
<Image
|
||||
className="size-4"
|
||||
src={
|
||||
retrievalMethod === RETRIEVE_METHOD.fullText
|
||||
? retrievalIcon.fullText
|
||||
: retrievalMethod === RETRIEVE_METHOD.hybrid
|
||||
? retrievalIcon.hybrid
|
||||
: retrievalIcon.vector
|
||||
}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], indexingType, retrievalMethod }) => {
|
||||
@ -257,11 +261,11 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='flex flex-col gap-y-3'>
|
||||
<div className='system-md-semibold-uppercase flex items-center gap-x-1 text-text-secondary'>
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="system-md-semibold-uppercase flex items-center gap-x-1 text-text-secondary">
|
||||
{isEmbedding && (
|
||||
<>
|
||||
<RiLoader2Fill className='size-4 animate-spin' />
|
||||
<RiLoader2Fill className="size-4 animate-spin" />
|
||||
<span>{t('datasetDocuments.embedding.processing')}</span>
|
||||
</>
|
||||
)}
|
||||
@ -269,18 +273,18 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
||||
</div>
|
||||
{
|
||||
enableBilling && plan.type !== Plan.team && (
|
||||
<div className='flex h-14 items-center rounded-xl border-[0.5px] border-black/5 bg-white p-3 shadow-md'>
|
||||
<div className='flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-[#FFF6ED]'>
|
||||
<ZapFast className='h-4 w-4 text-[#FB6514]' />
|
||||
<div className="flex h-14 items-center rounded-xl border-[0.5px] border-black/5 bg-white p-3 shadow-md">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-[#FFF6ED]">
|
||||
<ZapFast className="h-4 w-4 text-[#FB6514]" />
|
||||
</div>
|
||||
<div className='mx-3 grow text-[13px] font-medium text-gray-700'>
|
||||
<div className="mx-3 grow text-[13px] font-medium text-gray-700">
|
||||
{t('billing.plansCommon.documentProcessingPriorityUpgrade')}
|
||||
</div>
|
||||
<UpgradeBtn loc='knowledge-speed-up' />
|
||||
<UpgradeBtn loc="knowledge-speed-up" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='flex flex-col gap-0.5 pb-2'>
|
||||
<div className="flex flex-col gap-0.5 pb-2">
|
||||
{indexingStatusBatchDetail.map(indexingStatusDetail => (
|
||||
<div
|
||||
key={indexingStatusDetail.id}
|
||||
@ -291,84 +295,84 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
||||
>
|
||||
{isSourceEmbedding(indexingStatusDetail) && (
|
||||
<div
|
||||
className='absolute left-0 top-0 h-full min-w-0.5 border-r-[2px] border-r-components-progress-bar-progress-highlight bg-components-progress-bar-progress'
|
||||
className="absolute left-0 top-0 h-full min-w-0.5 border-r-[2px] border-r-components-progress-bar-progress-highlight bg-components-progress-bar-progress"
|
||||
style={{ width: `${getSourcePercent(indexingStatusDetail)}%` }}
|
||||
/>
|
||||
)}
|
||||
<div className='z-[1] flex h-full items-center gap-1 pl-[6px] pr-2'>
|
||||
<div className="z-[1] flex h-full items-center gap-1 pl-[6px] pr-2">
|
||||
{getSourceType(indexingStatusDetail.id) === DataSourceType.FILE && (
|
||||
<DocumentFileIcon
|
||||
size='sm'
|
||||
className='shrink-0'
|
||||
size="sm"
|
||||
className="shrink-0"
|
||||
name={getSourceName(indexingStatusDetail.id)}
|
||||
extension={getFileType(getSourceName(indexingStatusDetail.id))}
|
||||
/>
|
||||
)}
|
||||
{getSourceType(indexingStatusDetail.id) === DataSourceType.NOTION && (
|
||||
<NotionIcon
|
||||
className='shrink-0'
|
||||
type='page'
|
||||
className="shrink-0"
|
||||
type="page"
|
||||
src={getIcon(indexingStatusDetail.id)}
|
||||
/>
|
||||
)}
|
||||
<div className='flex w-0 grow items-center gap-1' title={getSourceName(indexingStatusDetail.id)}>
|
||||
<div className='system-xs-medium truncate text-text-secondary'>
|
||||
<div className="flex w-0 grow items-center gap-1" title={getSourceName(indexingStatusDetail.id)}>
|
||||
<div className="system-xs-medium truncate text-text-secondary">
|
||||
{getSourceName(indexingStatusDetail.id)}
|
||||
</div>
|
||||
{
|
||||
enableBilling && (
|
||||
<PriorityLabel className='ml-0' />
|
||||
<PriorityLabel className="ml-0" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{isSourceEmbedding(indexingStatusDetail) && (
|
||||
<div className='shrink-0 text-xs text-text-secondary'>{`${getSourcePercent(indexingStatusDetail)}%`}</div>
|
||||
<div className="shrink-0 text-xs text-text-secondary">{`${getSourcePercent(indexingStatusDetail)}%`}</div>
|
||||
)}
|
||||
{indexingStatusDetail.indexing_status === 'error' && (
|
||||
<Tooltip
|
||||
popupClassName='px-4 py-[14px] max-w-60 body-xs-regular text-text-secondary border-[0.5px] border-components-panel-border rounded-xl'
|
||||
popupClassName="px-4 py-[14px] max-w-60 body-xs-regular text-text-secondary border-[0.5px] border-components-panel-border rounded-xl"
|
||||
offset={4}
|
||||
popupContent={indexingStatusDetail.error}
|
||||
>
|
||||
<span>
|
||||
<RiErrorWarningFill className='size-4 shrink-0 text-text-destructive' />
|
||||
<RiErrorWarningFill className="size-4 shrink-0 text-text-destructive" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{indexingStatusDetail.indexing_status === 'completed' && (
|
||||
<RiCheckboxCircleFill className='size-4 shrink-0 text-text-success' />
|
||||
<RiCheckboxCircleFill className="size-4 shrink-0 text-text-success" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Divider type='horizontal' className='my-0 bg-divider-subtle' />
|
||||
<Divider type="horizontal" className="my-0 bg-divider-subtle" />
|
||||
<RuleDetail
|
||||
sourceData={ruleDetail}
|
||||
indexingType={indexingType}
|
||||
retrievalMethod={retrievalMethod}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-6 flex items-center gap-x-2 py-2'>
|
||||
<div className="mt-6 flex items-center gap-x-2 py-2">
|
||||
<Link
|
||||
href={apiReferenceUrl}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button
|
||||
className='w-fit gap-x-0.5 px-3'
|
||||
className="w-fit gap-x-0.5 px-3"
|
||||
>
|
||||
<RiTerminalBoxLine className='size-4' />
|
||||
<span className='px-0.5'>Access the API</span>
|
||||
<RiTerminalBoxLine className="size-4" />
|
||||
<span className="px-0.5">Access the API</span>
|
||||
</Button>
|
||||
</Link>
|
||||
<Button
|
||||
className='w-fit gap-x-0.5 px-3'
|
||||
variant='primary'
|
||||
className="w-fit gap-x-0.5 px-3"
|
||||
variant="primary"
|
||||
onClick={navToDocumentList}
|
||||
>
|
||||
<span className='px-0.5'>{t('datasetCreation.stepThree.navTo')}</span>
|
||||
<RiArrowRightLine className='size-4 stroke-current stroke-1' />
|
||||
<span className="px-0.5">{t('datasetCreation.stepThree.navTo')}</span>
|
||||
<RiArrowRightLine className="size-4 stroke-current stroke-1" />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { MockedFunction } from 'vitest'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import EmptyDatasetCreationModal from './index'
|
||||
import { createEmptyDataset } from '@/service/datasets'
|
||||
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
|
||||
import EmptyDatasetCreationModal from './index'
|
||||
|
||||
// Mock Next.js router
|
||||
const mockPush = vi.fn()
|
||||
@ -38,7 +38,7 @@ const mockInvalidDatasetList = vi.fn()
|
||||
const mockUseInvalidDatasetList = useInvalidDatasetList as MockedFunction<typeof useInvalidDatasetList>
|
||||
|
||||
// Test data builder for props
|
||||
const createDefaultProps = (overrides?: Partial<{ show: boolean; onHide: () => void }>) => ({
|
||||
const createDefaultProps = (overrides?: Partial<{ show: boolean, onHide: () => void }>) => ({
|
||||
show: true,
|
||||
onHide: vi.fn(),
|
||||
...overrides,
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import s from './index.module.css'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { trackEvent } from '@/app/components/base/amplitude'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
import Input from '@/app/components/base/input'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
|
||||
import { createEmptyDataset } from '@/service/datasets'
|
||||
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
|
||||
import { trackEvent } from '@/app/components/base/amplitude'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import s from './index.module.css'
|
||||
|
||||
type IProps = {
|
||||
show: boolean
|
||||
@ -68,9 +68,9 @@ const EmptyDatasetCreationModal = ({
|
||||
<div className={s.label}>{t('datasetCreation.stepOne.modal.input')}</div>
|
||||
<Input value={inputValue} placeholder={t('datasetCreation.stepOne.modal.placeholder') || ''} onChange={e => setInputValue(e.target.value)} />
|
||||
</div>
|
||||
<div className='flex flex-row-reverse'>
|
||||
<Button className='ml-2 w-24' variant='primary' onClick={submit}>{t('datasetCreation.stepOne.modal.confirmButton')}</Button>
|
||||
<Button className='w-24' onClick={onHide}>{t('datasetCreation.stepOne.modal.cancelButton')}</Button>
|
||||
<div className="flex flex-row-reverse">
|
||||
<Button className="ml-2 w-24" variant="primary" onClick={submit}>{t('datasetCreation.stepOne.modal.confirmButton')}</Button>
|
||||
<Button className="w-24" onClick={onHide}>{t('datasetCreation.stepOne.modal.cancelButton')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { MockedFunction } from 'vitest'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import FilePreview from './index'
|
||||
import type { CustomFile as File } from '@/models/datasets'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fetchFilePreview } from '@/service/common'
|
||||
import FilePreview from './index'
|
||||
|
||||
// Mock the fetchFilePreview service
|
||||
vi.mock('@/service/common', () => ({
|
||||
@ -31,7 +31,7 @@ const createMockFile = (overrides: Partial<File> = {}): File => {
|
||||
}
|
||||
|
||||
// Helper to render FilePreview with default props
|
||||
const renderFilePreview = (props: Partial<{ file?: File; hidePreview: () => void }> = {}) => {
|
||||
const renderFilePreview = (props: Partial<{ file?: File, hidePreview: () => void }> = {}) => {
|
||||
const defaultProps = {
|
||||
file: createMockFile(),
|
||||
hidePreview: vi.fn(),
|
||||
@ -705,8 +705,7 @@ describe('FilePreview', () => {
|
||||
it('should handle rapid file changes', async () => {
|
||||
// Arrange
|
||||
const files = Array.from({ length: 5 }, (_, i) =>
|
||||
createMockFile({ id: `file-${i}` }),
|
||||
)
|
||||
createMockFile({ id: `file-${i}` }))
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
import type { CustomFile as File } from '@/models/datasets'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import s from './index.module.css'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CustomFile as File } from '@/models/datasets'
|
||||
import { fetchFilePreview } from '@/service/common'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import s from './index.module.css'
|
||||
|
||||
type IProps = {
|
||||
file?: File
|
||||
@ -49,16 +49,20 @@ const FilePreview = ({
|
||||
<div className={cn(s.previewHeader)}>
|
||||
<div className={cn(s.title, 'title-md-semi-bold')}>
|
||||
<span>{t('datasetCreation.stepOne.filePreview')}</span>
|
||||
<div className='flex h-6 w-6 cursor-pointer items-center justify-center' onClick={hidePreview}>
|
||||
<XMarkIcon className='h-4 w-4'></XMarkIcon>
|
||||
<div className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={hidePreview}>
|
||||
<XMarkIcon className="h-4 w-4"></XMarkIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.fileName, 'system-xs-medium')}>
|
||||
<span>{getFileName(file)}</span><span className={cn(s.filetype)}>.{file?.extension}</span>
|
||||
<span>{getFileName(file)}</span>
|
||||
<span className={cn(s.filetype)}>
|
||||
.
|
||||
{file?.extension}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.previewContent)}>
|
||||
{loading && <Loading type='area' />}
|
||||
{loading && <Loading type="area" />}
|
||||
{!loading && (
|
||||
<div className={cn(s.fileContent, 'body-md-regular')}>{previewContent}</div>
|
||||
)}
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
'use client'
|
||||
import type { CustomFile as File, FileItem } from '@/models/datasets'
|
||||
import { RiDeleteBinLine, RiUploadCloud2Line } from '@remixicon/react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { RiDeleteBinLine, RiUploadCloud2Line } from '@remixicon/react'
|
||||
import DocumentFileIcon from '../../common/document-file-icon'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CustomFile as File, FileItem } from '@/models/datasets'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { getFileUploadErrorMessage } from '@/app/components/base/file-uploader/utils'
|
||||
import SimplePieChart from '@/app/components/base/simple-pie-chart'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
import I18n from '@/context/i18n'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
import { upload } from '@/service/base'
|
||||
import { useFileSupportTypes, useFileUploadConfig } from '@/service/use-common'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { Theme } from '@/types/app'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { getFileUploadErrorMessage } from '@/app/components/base/file-uploader/utils'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import DocumentFileIcon from '../../common/document-file-icon'
|
||||
|
||||
type IFileUploaderProps = {
|
||||
fileList: FileItem[]
|
||||
@ -244,17 +244,20 @@ const FileUploader = ({
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setDragging(false)
|
||||
if (!e.dataTransfer) return
|
||||
if (!e.dataTransfer)
|
||||
return
|
||||
const nested = await Promise.all(
|
||||
Array.from(e.dataTransfer.items).map((it) => {
|
||||
const entry = (it as any).webkitGetAsEntry?.()
|
||||
if (entry) return traverseFileEntry(entry)
|
||||
if (entry)
|
||||
return traverseFileEntry(entry)
|
||||
const f = it.getAsFile?.()
|
||||
return f ? Promise.resolve([f]) : Promise.resolve([])
|
||||
}),
|
||||
)
|
||||
let files = nested.flat()
|
||||
if (!supportBatchUpload) files = files.slice(0, 1)
|
||||
if (!supportBatchUpload)
|
||||
files = files.slice(0, 1)
|
||||
files = files.slice(0, fileUploadConfig.batch_count_limit)
|
||||
const valid = files.filter(isValid)
|
||||
initialUpload(valid)
|
||||
@ -314,7 +317,7 @@ const FileUploader = ({
|
||||
{!hideUpload && (
|
||||
<div ref={dropRef} className={cn('relative mb-2 box-border flex min-h-20 max-w-[640px] flex-col items-center justify-center gap-1 rounded-xl border border-dashed border-components-dropzone-border bg-components-dropzone-bg px-4 py-3 text-xs leading-4 text-text-tertiary', dragging && 'border-components-dropzone-border-accent bg-components-dropzone-bg-accent')}>
|
||||
<div className="flex min-h-5 items-center justify-center text-sm leading-4 text-text-secondary">
|
||||
<RiUploadCloud2Line className='mr-2 size-5' />
|
||||
<RiUploadCloud2Line className="mr-2 size-5" />
|
||||
|
||||
<span>
|
||||
{supportBatchUpload ? t('datasetCreation.stepOne.uploader.button') : t('datasetCreation.stepOne.uploader.buttonSingleFile')}
|
||||
@ -323,16 +326,18 @@ const FileUploader = ({
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div>{t('datasetCreation.stepOne.uploader.tip', {
|
||||
size: fileUploadConfig.file_size_limit,
|
||||
supportTypes: supportTypesShowNames,
|
||||
batchCount: fileUploadConfig.batch_count_limit,
|
||||
totalCount: fileUploadConfig.file_upload_limit,
|
||||
})}</div>
|
||||
{dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
|
||||
<div>
|
||||
{t('datasetCreation.stepOne.uploader.tip', {
|
||||
size: fileUploadConfig.file_size_limit,
|
||||
supportTypes: supportTypesShowNames,
|
||||
batchCount: fileUploadConfig.batch_count_limit,
|
||||
totalCount: fileUploadConfig.file_upload_limit,
|
||||
})}
|
||||
</div>
|
||||
{dragging && <div ref={dragRef} className="absolute left-0 top-0 h-full w-full" />}
|
||||
</div>
|
||||
)}
|
||||
<div className='max-w-[640px] cursor-default space-y-1'>
|
||||
<div className="max-w-[640px] cursor-default space-y-1">
|
||||
|
||||
{fileList.map((fileItem, index) => (
|
||||
<div
|
||||
@ -345,19 +350,19 @@ const FileUploader = ({
|
||||
>
|
||||
<div className="flex w-12 shrink-0 items-center justify-center">
|
||||
<DocumentFileIcon
|
||||
size='xl'
|
||||
size="xl"
|
||||
className="shrink-0"
|
||||
name={fileItem.file.name}
|
||||
extension={getFileType(fileItem.file)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex shrink grow flex-col gap-0.5">
|
||||
<div className='flex w-full'>
|
||||
<div className="flex w-full">
|
||||
<div className="w-0 grow truncate text-sm leading-4 text-text-secondary">{fileItem.file.name}</div>
|
||||
</div>
|
||||
<div className="w-full truncate leading-3 text-text-tertiary">
|
||||
<span className='uppercase'>{getFileType(fileItem.file)}</span>
|
||||
<span className='px-1 text-text-quaternary'>·</span>
|
||||
<span className="uppercase">{getFileType(fileItem.file)}</span>
|
||||
<span className="px-1 text-text-quaternary">·</span>
|
||||
<span>{getFileSize(fileItem.file.size)}</span>
|
||||
{/* <span className='px-1 text-text-quaternary'>·</span>
|
||||
<span>10k characters</span> */}
|
||||
@ -371,11 +376,14 @@ const FileUploader = ({
|
||||
// <div className={s.percent}>{`${fileItem.progress}%`}</div>
|
||||
<SimplePieChart percentage={fileItem.progress} stroke={chartColor} fill={chartColor} animationDuration={0} />
|
||||
)}
|
||||
<span className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
removeFile(fileItem.fileID)
|
||||
}}>
|
||||
<RiDeleteBinLine className='size-4 text-text-tertiary' />
|
||||
<span
|
||||
className="flex h-6 w-6 cursor-pointer items-center justify-center"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
removeFile(fileItem.fileID)
|
||||
}}
|
||||
>
|
||||
<RiDeleteBinLine className="size-4 text-text-tertiary" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import GoldIcon from './assets/gold.svg'
|
||||
import Piggybank from './assets/piggy-bank-mod.svg'
|
||||
import Selection from './assets/selection-mod.svg'
|
||||
import Research from './assets/research-mod.svg'
|
||||
import PatternRecognition from './assets/pattern-recognition-mod.svg'
|
||||
import Piggybank from './assets/piggy-bank-mod.svg'
|
||||
import Research from './assets/research-mod.svg'
|
||||
import Selection from './assets/selection-mod.svg'
|
||||
|
||||
export const indexMethodIcon = {
|
||||
high_quality: GoldIcon,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import DatasetUpdateForm from './index'
|
||||
import { ChunkingMode, DataSourceType, DatasetPermission } from '@/models/datasets'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { DataSourceProvider } from '@/models/common'
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
import { ChunkingMode, DatasetPermission, DataSourceType } from '@/models/datasets'
|
||||
import { RETRIEVE_METHOD } from '@/types/app'
|
||||
import DatasetUpdateForm from './index'
|
||||
|
||||
// IndexingType values from step-two (defined here since we mock step-two)
|
||||
// Using type assertion to match the expected IndexingType enum from step-two
|
||||
@ -27,7 +27,7 @@ vi.mock('react-i18next', () => ({
|
||||
|
||||
// Mock next/link
|
||||
vi.mock('next/link', () => {
|
||||
return function MockLink({ children, href }: { children: React.ReactNode; href: string }) {
|
||||
return function MockLink({ children, href }: { children: React.ReactNode, href: string }) {
|
||||
return <a href={href}>{children}</a>
|
||||
}
|
||||
})
|
||||
@ -55,7 +55,7 @@ vi.mock('@/context/dataset-detail', () => ({
|
||||
}))
|
||||
|
||||
// Mock useDefaultModel hook
|
||||
let mockEmbeddingsDefaultModel: { model: string; provider: string } | undefined
|
||||
let mockEmbeddingsDefaultModel: { model: string, provider: string } | undefined
|
||||
vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
|
||||
useDefaultModel: () => ({
|
||||
data: mockEmbeddingsDefaultModel,
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
'use client'
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import type { CrawlOptions, CrawlResultItem, createDocumentResponse, FileItem } from '@/models/datasets'
|
||||
import { produce } from 'immer'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
import { DataSourceProvider } from '@/models/common'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import { useGetDefaultDataSourceListAuth } from '@/service/use-datasource'
|
||||
import AppUnavailable from '../../base/app-unavailable'
|
||||
import { ModelTypeEnum } from '../../header/account-setting/model-provider-page/declarations'
|
||||
import StepOne from './step-one'
|
||||
import StepTwo from './step-two'
|
||||
import StepThree from './step-three'
|
||||
import StepTwo from './step-two'
|
||||
import { TopBar } from './top-bar'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import type { CrawlOptions, CrawlResultItem, FileItem, createDocumentResponse } from '@/models/datasets'
|
||||
import { DataSourceProvider, type NotionPage } from '@/models/common'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { useGetDefaultDataSourceListAuth } from '@/service/use-datasource'
|
||||
import { produce } from 'immer'
|
||||
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
|
||||
type DatasetUpdateFormProps = {
|
||||
datasetId?: string
|
||||
@ -104,12 +105,12 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
|
||||
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
|
||||
|
||||
return (
|
||||
<div className='flex flex-col overflow-hidden bg-components-panel-bg' style={{ height: 'calc(100vh - 56px)' }}>
|
||||
<div className="flex flex-col overflow-hidden bg-components-panel-bg" style={{ height: 'calc(100vh - 56px)' }}>
|
||||
<TopBar activeIndex={step - 1} datasetId={datasetId} />
|
||||
<div style={{ height: 'calc(100% - 52px)' }}>
|
||||
{
|
||||
isLoadingAuthedDataSourceList && (
|
||||
<Loading type='app' />
|
||||
<Loading type="app" />
|
||||
)
|
||||
}
|
||||
{
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { MockedFunction } from 'vitest'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import NotionPagePreview from './index'
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fetchNotionPagePreview } from '@/service/datasets'
|
||||
import NotionPagePreview from './index'
|
||||
|
||||
// Mock the fetchNotionPagePreview service
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
@ -808,8 +808,7 @@ describe('NotionPagePreview', () => {
|
||||
it('should handle rapid page changes', async () => {
|
||||
// Arrange
|
||||
const pages = Array.from({ length: 5 }, (_, i) =>
|
||||
createMockNotionPage({ page_id: `page-${i}` }),
|
||||
)
|
||||
createMockNotionPage({ page_id: `page-${i}` }))
|
||||
|
||||
// Act
|
||||
const { rerender } = render(
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import s from './index.module.css'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import NotionIcon from '@/app/components/base/notion-icon'
|
||||
import { fetchNotionPagePreview } from '@/service/datasets'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import s from './index.module.css'
|
||||
|
||||
type IProps = {
|
||||
currentPage?: NotionPage
|
||||
@ -51,21 +51,21 @@ const NotionPagePreview = ({
|
||||
<div className={cn(s.previewHeader)}>
|
||||
<div className={cn(s.title, 'title-md-semi-bold')}>
|
||||
<span>{t('datasetCreation.stepOne.pagePreview')}</span>
|
||||
<div className='flex h-6 w-6 cursor-pointer items-center justify-center' onClick={hidePreview}>
|
||||
<XMarkIcon className='h-4 w-4'></XMarkIcon>
|
||||
<div className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={hidePreview}>
|
||||
<XMarkIcon className="h-4 w-4"></XMarkIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.fileName, 'system-xs-medium')}>
|
||||
<NotionIcon
|
||||
className='mr-1 shrink-0'
|
||||
type='page'
|
||||
className="mr-1 shrink-0"
|
||||
type="page"
|
||||
src={currentPage?.page_icon}
|
||||
/>
|
||||
{currentPage?.page_name}
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.previewContent, 'body-md-regular')}>
|
||||
{loading && <Loading type='area' />}
|
||||
{loading && <Loading type="area" />}
|
||||
{!loading && (
|
||||
<div className={cn(s.fileContent, 'body-md-regular')}>{previewContent}</div>
|
||||
)}
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
'use client'
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
import type { DataSourceProvider, NotionPage } from '@/models/common'
|
||||
import type { CrawlOptions, CrawlResultItem, FileItem } from '@/models/datasets'
|
||||
import { RiArrowRightLine, RiFolder6Line } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiArrowRightLine, RiFolder6Line } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import NotionConnector from '@/app/components/base/notion-connector'
|
||||
import { NotionPageSelector } from '@/app/components/base/notion-page-selector'
|
||||
import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import VectorSpaceFull from '@/app/components/billing/vector-space-full'
|
||||
import { ENABLE_WEBSITE_FIRECRAWL, ENABLE_WEBSITE_JINAREADER, ENABLE_WEBSITE_WATERCRAWL } from '@/config'
|
||||
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import EmptyDatasetCreationModal from '../empty-dataset-creation-modal'
|
||||
import FilePreview from '../file-preview'
|
||||
import FileUploader from '../file-uploader'
|
||||
import NotionPagePreview from '../notion-page-preview'
|
||||
import EmptyDatasetCreationModal from '../empty-dataset-creation-modal'
|
||||
import Website from '../website'
|
||||
import WebsitePreview from '../website/preview'
|
||||
import s from './index.module.css'
|
||||
import type { CrawlOptions, CrawlResultItem, FileItem } from '@/models/datasets'
|
||||
import type { DataSourceProvider, NotionPage } from '@/models/common'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { NotionPageSelector } from '@/app/components/base/notion-page-selector'
|
||||
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import VectorSpaceFull from '@/app/components/billing/vector-space-full'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { ENABLE_WEBSITE_FIRECRAWL, ENABLE_WEBSITE_JINAREADER, ENABLE_WEBSITE_WATERCRAWL } from '@/config'
|
||||
import NotionConnector from '@/app/components/base/notion-connector'
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import UpgradeCard from './upgrade-card'
|
||||
|
||||
type IStepOneProps = {
|
||||
@ -149,9 +149,11 @@ const StepOne = ({
|
||||
}, [files, isShowVectorSpaceFull])
|
||||
|
||||
const isNotionAuthed = useMemo(() => {
|
||||
if (!authedDataSourceList) return false
|
||||
if (!authedDataSourceList)
|
||||
return false
|
||||
const notionSource = authedDataSourceList.find(item => item.provider === 'notion_datasource')
|
||||
if (!notionSource) return false
|
||||
if (!notionSource)
|
||||
return false
|
||||
return notionSource.credentials_list.length > 0
|
||||
}, [authedDataSourceList])
|
||||
|
||||
@ -160,10 +162,10 @@ const StepOne = ({
|
||||
}, [authedDataSourceList])
|
||||
|
||||
return (
|
||||
<div className='h-full w-full overflow-x-auto'>
|
||||
<div className='flex h-full w-full min-w-[1440px]'>
|
||||
<div className='relative h-full w-1/2 overflow-y-auto'>
|
||||
<div className='flex justify-end'>
|
||||
<div className="h-full w-full overflow-x-auto">
|
||||
<div className="flex h-full w-full min-w-[1440px]">
|
||||
<div className="relative h-full w-1/2 overflow-y-auto">
|
||||
<div className="flex justify-end">
|
||||
<div className={cn(s.form)}>
|
||||
{
|
||||
shouldShowDataSourceTypeList && (
|
||||
@ -174,7 +176,7 @@ const StepOne = ({
|
||||
}
|
||||
{
|
||||
shouldShowDataSourceTypeList && (
|
||||
<div className='mb-8 grid grid-cols-3 gap-4'>
|
||||
<div className="mb-8 grid grid-cols-3 gap-4">
|
||||
<div
|
||||
className={cn(
|
||||
s.dataSourceItem,
|
||||
@ -193,7 +195,7 @@ const StepOne = ({
|
||||
<span className={cn(s.datasetIcon)} />
|
||||
<span
|
||||
title={t('datasetCreation.stepOne.dataSourceType.file')!}
|
||||
className='truncate'
|
||||
className="truncate"
|
||||
>
|
||||
{t('datasetCreation.stepOne.dataSourceType.file')}
|
||||
</span>
|
||||
@ -216,7 +218,7 @@ const StepOne = ({
|
||||
<span className={cn(s.datasetIcon, s.notion)} />
|
||||
<span
|
||||
title={t('datasetCreation.stepOne.dataSourceType.notion')!}
|
||||
className='truncate'
|
||||
className="truncate"
|
||||
>
|
||||
{t('datasetCreation.stepOne.dataSourceType.notion')}
|
||||
</span>
|
||||
@ -240,7 +242,7 @@ const StepOne = ({
|
||||
<span className={cn(s.datasetIcon, s.web)} />
|
||||
<span
|
||||
title={t('datasetCreation.stepOne.dataSourceType.web')!}
|
||||
className='truncate'
|
||||
className="truncate"
|
||||
>
|
||||
{t('datasetCreation.stepOne.dataSourceType.web')}
|
||||
</span>
|
||||
@ -261,12 +263,12 @@ const StepOne = ({
|
||||
supportBatchUpload={supportBatchUpload}
|
||||
/>
|
||||
{isShowVectorSpaceFull && (
|
||||
<div className='mb-4 max-w-[640px]'>
|
||||
<div className="mb-4 max-w-[640px]">
|
||||
<VectorSpaceFull />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex max-w-[640px] justify-end gap-2">
|
||||
<Button disabled={nextDisabled} variant='primary' onClick={onStepChange}>
|
||||
<Button disabled={nextDisabled} variant="primary" onClick={onStepChange}>
|
||||
<span className="flex gap-0.5 px-[10px]">
|
||||
<span className="px-0.5">{t('datasetCreation.stepOne.button')}</span>
|
||||
<RiArrowRightLine className="size-4" />
|
||||
@ -275,8 +277,8 @@ const StepOne = ({
|
||||
</div>
|
||||
{
|
||||
enableBilling && plan.type === Plan.sandbox && files.length > 0 && (
|
||||
<div className='mt-5'>
|
||||
<div className='mb-4 h-px bg-divider-subtle'></div>
|
||||
<div className="mt-5">
|
||||
<div className="mb-4 h-px bg-divider-subtle"></div>
|
||||
<UpgradeCard />
|
||||
</div>
|
||||
)
|
||||
@ -288,7 +290,7 @@ const StepOne = ({
|
||||
{!isNotionAuthed && <NotionConnector onSetting={onSetting} />}
|
||||
{isNotionAuthed && (
|
||||
<>
|
||||
<div className='mb-8 w-[640px]'>
|
||||
<div className="mb-8 w-[640px]">
|
||||
<NotionPageSelector
|
||||
value={notionPages.map(page => page.page_id)}
|
||||
onSelect={updateNotionPages}
|
||||
@ -299,12 +301,12 @@ const StepOne = ({
|
||||
/>
|
||||
</div>
|
||||
{isShowVectorSpaceFull && (
|
||||
<div className='mb-4 max-w-[640px]'>
|
||||
<div className="mb-4 max-w-[640px]">
|
||||
<VectorSpaceFull />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex max-w-[640px] justify-end gap-2">
|
||||
<Button disabled={isShowVectorSpaceFull || !notionPages.length} variant='primary' onClick={onStepChange}>
|
||||
<Button disabled={isShowVectorSpaceFull || !notionPages.length} variant="primary" onClick={onStepChange}>
|
||||
<span className="flex gap-0.5 px-[10px]">
|
||||
<span className="px-0.5">{t('datasetCreation.stepOne.button')}</span>
|
||||
<RiArrowRightLine className="size-4" />
|
||||
@ -330,12 +332,12 @@ const StepOne = ({
|
||||
/>
|
||||
</div>
|
||||
{isShowVectorSpaceFull && (
|
||||
<div className='mb-4 max-w-[640px]'>
|
||||
<div className="mb-4 max-w-[640px]">
|
||||
<VectorSpaceFull />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex max-w-[640px] justify-end gap-2">
|
||||
<Button disabled={isShowVectorSpaceFull || !websitePages.length} variant='primary' onClick={onStepChange}>
|
||||
<Button disabled={isShowVectorSpaceFull || !websitePages.length} variant="primary" onClick={onStepChange}>
|
||||
<span className="flex gap-0.5 px-[10px]">
|
||||
<span className="px-0.5">{t('datasetCreation.stepOne.button')}</span>
|
||||
<RiArrowRightLine className="size-4" />
|
||||
@ -346,7 +348,7 @@ const StepOne = ({
|
||||
)}
|
||||
{!datasetId && (
|
||||
<>
|
||||
<div className='my-8 h-px max-w-[640px] bg-divider-regular' />
|
||||
<div className="my-8 h-px max-w-[640px] bg-divider-regular" />
|
||||
<span className="inline-flex cursor-pointer items-center text-[13px] leading-4 text-text-accent" onClick={modalShowHandle}>
|
||||
<RiFolder6Line className="mr-1 size-4" />
|
||||
{t('datasetCreation.stepOne.emptyDatasetCreation')}
|
||||
@ -357,7 +359,7 @@ const StepOne = ({
|
||||
<EmptyDatasetCreationModal show={showModal} onHide={modalCloseHandle} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='h-full w-1/2 overflow-y-auto'>
|
||||
<div className="h-full w-1/2 overflow-y-auto">
|
||||
{currentFile && <FilePreview file={currentFile} hidePreview={hideFilePreview} />}
|
||||
{currentNotionPage && (
|
||||
<NotionPagePreview
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
|
||||
const UpgradeCard: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
@ -14,17 +14,17 @@ const UpgradeCard: FC = () => {
|
||||
}, [setShowPricingModal])
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-between rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg py-3 pl-4 pr-3.5 shadow-xs backdrop-blur-[5px] '>
|
||||
<div className="flex items-center justify-between rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg py-3 pl-4 pr-3.5 shadow-xs backdrop-blur-[5px] ">
|
||||
<div>
|
||||
<div className='title-md-semi-bold bg-[linear-gradient(92deg,_var(--components-input-border-active-prompt-1,_#0BA5EC)_0%,_var(--components-input-border-active-prompt-2,_#155AEF)_99.21%)] bg-clip-text text-transparent'>{t('billing.upgrade.uploadMultipleFiles.title')}</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('billing.upgrade.uploadMultipleFiles.description')}</div>
|
||||
<div className="title-md-semi-bold bg-[linear-gradient(92deg,_var(--components-input-border-active-prompt-1,_#0BA5EC)_0%,_var(--components-input-border-active-prompt-2,_#155AEF)_99.21%)] bg-clip-text text-transparent">{t('billing.upgrade.uploadMultipleFiles.title')}</div>
|
||||
<div className="system-xs-regular text-text-tertiary">{t('billing.upgrade.uploadMultipleFiles.description')}</div>
|
||||
</div>
|
||||
<UpgradeBtn
|
||||
size='custom'
|
||||
size="custom"
|
||||
isShort
|
||||
className='ml-3 !h-8 !rounded-lg px-2'
|
||||
labelKey='billing.triggerLimitModal.upgrade'
|
||||
loc='upload-multiple-files'
|
||||
className="ml-3 !h-8 !rounded-lg px-2"
|
||||
labelKey="billing.triggerLimitModal.upgrade"
|
||||
loc="upload-multiple-files"
|
||||
onClick={handleUpgrade}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { createDocumentResponse, FullDocumentDetail, IconInfo } from '@/models/datasets'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import StepThree from './index'
|
||||
import type { FullDocumentDetail, IconInfo, createDocumentResponse } from '@/models/datasets'
|
||||
|
||||
// Mock the EmbeddingProcess component since it has complex async logic
|
||||
vi.mock('../embedding-process', () => ({
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
'use client'
|
||||
import type { createDocumentResponse, FullDocumentDetail } from '@/models/datasets'
|
||||
import { RiBookOpenLine } from '@remixicon/react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiBookOpenLine } from '@remixicon/react'
|
||||
import EmbeddingProcess from '../embedding-process'
|
||||
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import EmbeddingProcess from '../embedding-process'
|
||||
|
||||
type StepThreeProps = {
|
||||
datasetId?: string
|
||||
@ -32,40 +32,40 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex h-full max-h-full w-full justify-center overflow-y-auto'>
|
||||
<div className='h-full max-w-[960px] shrink-0 grow overflow-y-auto px-14 sm:px-16'>
|
||||
<div className='mx-auto max-w-[640px] pb-8 pt-10'>
|
||||
<div className="flex h-full max-h-full w-full justify-center overflow-y-auto">
|
||||
<div className="h-full max-w-[960px] shrink-0 grow overflow-y-auto px-14 sm:px-16">
|
||||
<div className="mx-auto max-w-[640px] pb-8 pt-10">
|
||||
{!datasetId && (
|
||||
<>
|
||||
<div className='flex flex-col gap-y-1 pb-3'>
|
||||
<div className='title-2xl-semi-bold text-text-primary'>{t('datasetCreation.stepThree.creationTitle')}</div>
|
||||
<div className='system-sm-regular text-text-tertiary'>{t('datasetCreation.stepThree.creationContent')}</div>
|
||||
<div className="flex flex-col gap-y-1 pb-3">
|
||||
<div className="title-2xl-semi-bold text-text-primary">{t('datasetCreation.stepThree.creationTitle')}</div>
|
||||
<div className="system-sm-regular text-text-tertiary">{t('datasetCreation.stepThree.creationContent')}</div>
|
||||
</div>
|
||||
<div className='flex items-center gap-x-4'>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<AppIcon
|
||||
size='xxl'
|
||||
size="xxl"
|
||||
iconType={iconInfo.icon_type}
|
||||
icon={iconInfo.icon}
|
||||
background={iconInfo.icon_background}
|
||||
imageUrl={iconInfo.icon_url}
|
||||
className='shrink-0'
|
||||
className="shrink-0"
|
||||
/>
|
||||
<div className='flex grow flex-col gap-y-1'>
|
||||
<div className='system-sm-semibold flex h-6 items-center text-text-secondary'>
|
||||
<div className="flex grow flex-col gap-y-1">
|
||||
<div className="system-sm-semibold flex h-6 items-center text-text-secondary">
|
||||
{t('datasetCreation.stepThree.label')}
|
||||
</div>
|
||||
<div className='system-sm-regular w-full truncate rounded-lg bg-components-input-bg-normal p-2 text-components-input-text-filled'>
|
||||
<span className='px-1'>{datasetName || creationCache?.dataset?.name}</span>
|
||||
<div className="system-sm-regular w-full truncate rounded-lg bg-components-input-bg-normal p-2 text-components-input-text-filled">
|
||||
<span className="px-1">{datasetName || creationCache?.dataset?.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Divider type='horizontal' className='my-6 bg-divider-subtle' />
|
||||
<Divider type="horizontal" className="my-6 bg-divider-subtle" />
|
||||
</>
|
||||
)}
|
||||
{datasetId && (
|
||||
<div className='flex flex-col gap-y-1 pb-3'>
|
||||
<div className='title-2xl-semi-bold text-text-primary'>{t('datasetCreation.stepThree.additionTitle')}</div>
|
||||
<div className='system-sm-regular text-text-tertiary'>{`${t('datasetCreation.stepThree.additionP1')} ${datasetName || creationCache?.dataset?.name} ${t('datasetCreation.stepThree.additionP2')}`}</div>
|
||||
<div className="flex flex-col gap-y-1 pb-3">
|
||||
<div className="title-2xl-semi-bold text-text-primary">{t('datasetCreation.stepThree.additionTitle')}</div>
|
||||
<div className="system-sm-regular text-text-tertiary">{`${t('datasetCreation.stepThree.additionP1')} ${datasetName || creationCache?.dataset?.name} ${t('datasetCreation.stepThree.additionP2')}`}</div>
|
||||
</div>
|
||||
)}
|
||||
<EmbeddingProcess
|
||||
@ -78,18 +78,18 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
|
||||
</div>
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<div className='shrink-0 pr-8 pt-[88px] text-xs'>
|
||||
<div className='flex w-[328px] flex-col gap-3 rounded-xl bg-background-section p-6 text-text-tertiary'>
|
||||
<div className='flex size-10 items-center justify-center rounded-[10px] bg-components-card-bg shadow-lg'>
|
||||
<RiBookOpenLine className='size-5 text-text-accent' />
|
||||
<div className="shrink-0 pr-8 pt-[88px] text-xs">
|
||||
<div className="flex w-[328px] flex-col gap-3 rounded-xl bg-background-section p-6 text-text-tertiary">
|
||||
<div className="flex size-10 items-center justify-center rounded-[10px] bg-components-card-bg shadow-lg">
|
||||
<RiBookOpenLine className="size-5 text-text-accent" />
|
||||
</div>
|
||||
<div className='text-base font-semibold text-text-secondary'>{t('datasetCreation.stepThree.sideTipTitle')}</div>
|
||||
<div className='text-text-tertiary'>{t('datasetCreation.stepThree.sideTipContent')}</div>
|
||||
<div className="text-base font-semibold text-text-secondary">{t('datasetCreation.stepThree.sideTipTitle')}</div>
|
||||
<div className="text-text-tertiary">{t('datasetCreation.stepThree.sideTipContent')}</div>
|
||||
<a
|
||||
href={docLink('/guides/knowledge-base/integrate-knowledge-within-application')}
|
||||
target='_blank'
|
||||
rel='noreferrer noopener'
|
||||
className='system-sm-regular text-text-accent'
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="system-sm-regular text-text-accent"
|
||||
>
|
||||
{t('datasetPipeline.addDocuments.stepThree.learnMore')}
|
||||
</a>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,79 +1,96 @@
|
||||
import type { FC, PropsWithChildren, ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { InputProps } from '@/app/components/base/input'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import type { InputNumberProps } from '@/app/components/base/input-number'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { InputNumber } from '@/app/components/base/input-number'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
const TextLabel: FC<PropsWithChildren> = (props) => {
|
||||
return <label className='text-xs font-semibold leading-none text-text-secondary'>{props.children}</label>
|
||||
return <label className="text-xs font-semibold leading-none text-text-secondary">{props.children}</label>
|
||||
}
|
||||
|
||||
const FormField: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
|
||||
return <div className='flex-1 space-y-2'>
|
||||
<TextLabel>{props.label}</TextLabel>
|
||||
{props.children}
|
||||
</div>
|
||||
return (
|
||||
<div className="flex-1 space-y-2">
|
||||
<TextLabel>{props.label}</TextLabel>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const DelimiterInput: FC<InputProps & { tooltip?: string }> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
return <FormField label={<div className='mb-1 flex items-center'>
|
||||
<span className='system-sm-semibold mr-0.5'>{t('datasetCreation.stepTwo.separator')}</span>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='max-w-[200px]'>
|
||||
{props.tooltip || t('datasetCreation.stepTwo.separatorTip')}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>}>
|
||||
<Input
|
||||
type="text"
|
||||
className='h-9'
|
||||
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder')!}
|
||||
{...props}
|
||||
/>
|
||||
</FormField>
|
||||
return (
|
||||
<FormField label={(
|
||||
<div className="mb-1 flex items-center">
|
||||
<span className="system-sm-semibold mr-0.5">{t('datasetCreation.stepTwo.separator')}</span>
|
||||
<Tooltip
|
||||
popupContent={(
|
||||
<div className="max-w-[200px]">
|
||||
{props.tooltip || t('datasetCreation.stepTwo.separatorTip')}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
className="h-9"
|
||||
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder')!}
|
||||
{...props}
|
||||
/>
|
||||
</FormField>
|
||||
)
|
||||
}
|
||||
|
||||
export const MaxLengthInput: FC<InputNumberProps> = (props) => {
|
||||
const maxValue = Number.parseInt(globalThis.document?.body?.getAttribute('data-public-indexing-max-segmentation-tokens-length') || '4000', 10)
|
||||
|
||||
const { t } = useTranslation()
|
||||
return <FormField label={<div className='system-sm-semibold mb-1'>
|
||||
{t('datasetCreation.stepTwo.maxLength')}
|
||||
</div>}>
|
||||
<InputNumber
|
||||
type="number"
|
||||
size='large'
|
||||
placeholder={`≤ ${maxValue}`}
|
||||
max={maxValue}
|
||||
min={1}
|
||||
{...props}
|
||||
/>
|
||||
</FormField>
|
||||
return (
|
||||
<FormField label={(
|
||||
<div className="system-sm-semibold mb-1">
|
||||
{t('datasetCreation.stepTwo.maxLength')}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<InputNumber
|
||||
type="number"
|
||||
size="large"
|
||||
placeholder={`≤ ${maxValue}`}
|
||||
max={maxValue}
|
||||
min={1}
|
||||
{...props}
|
||||
/>
|
||||
</FormField>
|
||||
)
|
||||
}
|
||||
|
||||
export const OverlapInput: FC<InputNumberProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
return <FormField label={<div className='mb-1 flex items-center'>
|
||||
<span className='system-sm-semibold'>{t('datasetCreation.stepTwo.overlap')}</span>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='max-w-[200px]'>
|
||||
{t('datasetCreation.stepTwo.overlapTip')}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>}>
|
||||
<InputNumber
|
||||
type="number"
|
||||
size='large'
|
||||
placeholder={t('datasetCreation.stepTwo.overlap') || ''}
|
||||
min={1}
|
||||
{...props}
|
||||
/>
|
||||
</FormField>
|
||||
return (
|
||||
<FormField label={(
|
||||
<div className="mb-1 flex items-center">
|
||||
<span className="system-sm-semibold">{t('datasetCreation.stepTwo.overlap')}</span>
|
||||
<Tooltip
|
||||
popupContent={(
|
||||
<div className="max-w-[200px]">
|
||||
{t('datasetCreation.stepTwo.overlapTip')}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<InputNumber
|
||||
type="number"
|
||||
size="large"
|
||||
placeholder={t('datasetCreation.stepTwo.overlap') || ''}
|
||||
min={1}
|
||||
{...props}
|
||||
/>
|
||||
</FormField>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { ILanguageSelectProps } from './index'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import LanguageSelect from './index'
|
||||
import type { ILanguageSelectProps } from './index'
|
||||
import { languages } from '@/i18n-config/language'
|
||||
import LanguageSelect from './index'
|
||||
|
||||
// Get supported languages for test assertions
|
||||
const supportedLanguages = languages.filter(lang => lang.supported)
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import React from 'react'
|
||||
import Popover from '@/app/components/base/popover'
|
||||
import { languages } from '@/i18n-config/language'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
export type ILanguageSelectProps = {
|
||||
currentLanguage: string
|
||||
@ -20,42 +20,44 @@ const LanguageSelect: FC<ILanguageSelectProps> = ({
|
||||
return (
|
||||
<Popover
|
||||
manualClose
|
||||
trigger='click'
|
||||
trigger="click"
|
||||
disabled={disabled}
|
||||
popupClassName='z-20'
|
||||
htmlContent={
|
||||
<div className='w-full p-1'>
|
||||
popupClassName="z-20"
|
||||
htmlContent={(
|
||||
<div className="w-full p-1">
|
||||
{languages.filter(language => language.supported).map(({ prompt_name }) => (
|
||||
<div
|
||||
key={prompt_name}
|
||||
className='inline-flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 hover:bg-state-base-hover'
|
||||
className="inline-flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 hover:bg-state-base-hover"
|
||||
onClick={() => onSelect(prompt_name)}
|
||||
>
|
||||
<span className='system-sm-medium text-text-secondary'>{prompt_name}</span>
|
||||
{(currentLanguage === prompt_name) && <RiCheckLine className='size-4 text-text-accent' />}
|
||||
<span className="system-sm-medium text-text-secondary">{prompt_name}</span>
|
||||
{(currentLanguage === prompt_name) && <RiCheckLine className="size-4 text-text-accent" />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
btnElement={
|
||||
)}
|
||||
btnElement={(
|
||||
<div className={cn('inline-flex items-center gap-x-[1px]', disabled && 'cursor-not-allowed')}>
|
||||
<span className={cn(
|
||||
'system-xs-semibold px-[3px] text-components-button-tertiary-text',
|
||||
disabled ? 'text-components-button-tertiary-text-disabled' : '',
|
||||
)}>
|
||||
)}
|
||||
>
|
||||
{currentLanguage}
|
||||
</span>
|
||||
<RiArrowDownSLine className={cn(
|
||||
'size-3.5 text-components-button-tertiary-text',
|
||||
disabled ? 'text-components-button-tertiary-text-disabled' : '',
|
||||
)} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
btnClassName={() => cn(
|
||||
'!hover:bg-components-button-tertiary-bg !mx-1 rounded-md !border-0 !bg-components-button-tertiary-bg !px-1.5 !py-1',
|
||||
disabled ? 'bg-components-button-tertiary-bg-disabled' : '',
|
||||
)}
|
||||
className='!left-1 !z-20 h-fit !w-[140px] !translate-x-0'
|
||||
className="!left-1 !z-20 h-fit !w-[140px] !translate-x-0"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -20,25 +20,25 @@ type OptionCardHeaderProps = {
|
||||
|
||||
export const OptionCardHeader: FC<OptionCardHeaderProps> = (props) => {
|
||||
const { icon, title, description, isActive, activeClassName, effectImg, disabled } = props
|
||||
return <div className={cn('relative flex h-full overflow-hidden rounded-t-xl',
|
||||
isActive && activeClassName,
|
||||
!disabled && 'cursor-pointer')}>
|
||||
<div className='relative flex size-14 items-center justify-center overflow-hidden'>
|
||||
{isActive && effectImg && <Image src={effectImg} className='absolute left-0 top-0 h-full w-full' alt='' width={56} height={56} />}
|
||||
<div className='p-1'>
|
||||
<div className='flex size-8 justify-center rounded-lg border border-components-panel-border-subtle bg-background-default-dodge p-1.5 shadow-md'>
|
||||
{icon}
|
||||
return (
|
||||
<div className={cn('relative flex h-full overflow-hidden rounded-t-xl', isActive && activeClassName, !disabled && 'cursor-pointer')}>
|
||||
<div className="relative flex size-14 items-center justify-center overflow-hidden">
|
||||
{isActive && effectImg && <Image src={effectImg} className="absolute left-0 top-0 h-full w-full" alt="" width={56} height={56} />}
|
||||
<div className="p-1">
|
||||
<div className="flex size-8 justify-center rounded-lg border border-components-panel-border-subtle bg-background-default-dodge p-1.5 shadow-md">
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TriangleArrow
|
||||
className={cn('absolute -bottom-1.5 left-4 text-transparent', isActive && 'text-components-panel-bg')}
|
||||
/>
|
||||
<div className="flex-1 space-y-0.5 py-3 pr-4">
|
||||
<div className="system-md-semibold text-text-secondary">{title}</div>
|
||||
<div className="system-xs-regular text-text-tertiary">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<TriangleArrow
|
||||
className={cn('absolute -bottom-1.5 left-4 text-transparent', isActive && 'text-components-panel-bg')}
|
||||
/>
|
||||
<div className='flex-1 space-y-0.5 py-3 pr-4'>
|
||||
<div className='system-md-semibold text-text-secondary'>{title}</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type OptionCardProps = {
|
||||
@ -64,12 +64,9 @@ export const OptionCard: FC<OptionCardProps> = (
|
||||
const { icon, className, title, description, isActive, children, actions, activeHeaderClassName, style, effectImg, onSwitched, noHighlight, disabled, ...rest } = props
|
||||
return (
|
||||
<div
|
||||
className={cn('rounded-xl bg-components-option-card-option-bg shadow-xs',
|
||||
(isActive && !noHighlight)
|
||||
? 'border-[1.5px] border-components-option-card-option-selected-border'
|
||||
: 'border border-components-option-card-option-border',
|
||||
disabled && 'pointer-events-none opacity-50',
|
||||
className)}
|
||||
className={cn('rounded-xl bg-components-option-card-option-bg shadow-xs', (isActive && !noHighlight)
|
||||
? 'border-[1.5px] border-components-option-card-option-selected-border'
|
||||
: 'border border-components-option-card-option-border', disabled && 'pointer-events-none opacity-50', className)}
|
||||
style={{
|
||||
...style,
|
||||
}}
|
||||
@ -90,13 +87,16 @@ export const OptionCard: FC<OptionCardProps> = (
|
||||
disabled={disabled}
|
||||
/>
|
||||
{/** Body */}
|
||||
{isActive && (children || actions) && <div className='rounded-b-xl bg-components-panel-bg px-4 py-3'>
|
||||
{children}
|
||||
{actions && <div className='mt-4 flex gap-2'>
|
||||
{actions}
|
||||
{isActive && (children || actions) && (
|
||||
<div className="rounded-b-xl bg-components-panel-bg px-4 py-3">
|
||||
{children}
|
||||
{actions && (
|
||||
<div className="mt-4 flex gap-2">
|
||||
{actions}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</div>}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { IPreviewItemProps } from './index'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import PreviewItem, { PreviewType } from './index'
|
||||
import type { IPreviewItemProps } from './index'
|
||||
|
||||
// Test data builder for props
|
||||
const createDefaultProps = (overrides?: Partial<IPreviewItemProps>): IPreviewItemProps => ({
|
||||
|
||||
@ -44,29 +44,33 @@ const PreviewItem: FC<IPreviewItemProps> = ({
|
||||
const formattedIndex = (() => String(index).padStart(3, '0'))()
|
||||
|
||||
return (
|
||||
<div className='rounded-xl bg-gray-50 p-4'>
|
||||
<div className='flex h-5 items-center justify-between text-xs text-gray-500'>
|
||||
<div className='box-border flex h-[18px] items-center space-x-1 rounded-md border border-gray-200 pl-1 pr-1.5 font-medium italic'>
|
||||
<div className="rounded-xl bg-gray-50 p-4">
|
||||
<div className="flex h-5 items-center justify-between text-xs text-gray-500">
|
||||
<div className="box-border flex h-[18px] items-center space-x-1 rounded-md border border-gray-200 pl-1 pr-1.5 font-medium italic">
|
||||
{sharpIcon}
|
||||
<span>{formattedIndex}</span>
|
||||
</div>
|
||||
<div className='flex items-center space-x-1'>
|
||||
<div className="flex items-center space-x-1">
|
||||
{textIcon}
|
||||
<span>{charNums} {t('datasetCreation.stepTwo.characters')}</span>
|
||||
<span>
|
||||
{charNums}
|
||||
{' '}
|
||||
{t('datasetCreation.stepTwo.characters')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-2 line-clamp-6 max-h-[120px] overflow-hidden text-sm text-gray-800'>
|
||||
<div className="mt-2 line-clamp-6 max-h-[120px] overflow-hidden text-sm text-gray-800">
|
||||
{type === PreviewType.TEXT && (
|
||||
<div style={{ whiteSpace: 'pre-line' }}>{content}</div>
|
||||
)}
|
||||
{type === PreviewType.QA && (
|
||||
<div style={{ whiteSpace: 'pre-line' }}>
|
||||
<div className='flex'>
|
||||
<div className='text-medium mr-2 shrink-0 text-gray-400'>Q</div>
|
||||
<div className="flex">
|
||||
<div className="text-medium mr-2 shrink-0 text-gray-400">Q</div>
|
||||
<div style={{ whiteSpace: 'pre-line' }}>{qa?.question}</div>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<div className='text-medium mr-2 shrink-0 text-gray-400'>A</div>
|
||||
<div className="flex">
|
||||
<div className="text-medium mr-2 shrink-0 text-gray-400">A</div>
|
||||
<div style={{ whiteSpace: 'pre-line' }}>{qa?.answer}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import type { StepperProps } from './index'
|
||||
import type { Step, StepperStepProps } from './step'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { Stepper, type StepperProps } from './index'
|
||||
import { type Step, StepperStep, type StepperStepProps } from './step'
|
||||
import { Stepper } from './index'
|
||||
import { StepperStep } from './step'
|
||||
|
||||
// Test data factory for creating steps
|
||||
const createStep = (overrides: Partial<Step> = {}): Step => ({
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { type FC, Fragment } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import type { Step } from './step'
|
||||
import { Fragment } from 'react'
|
||||
import { StepperStep } from './step'
|
||||
|
||||
export type StepperProps = {
|
||||
@ -9,19 +10,21 @@ export type StepperProps = {
|
||||
|
||||
export const Stepper: FC<StepperProps> = (props) => {
|
||||
const { steps, activeIndex } = props
|
||||
return <div className='flex items-center gap-3'>
|
||||
{steps.map((step, index) => {
|
||||
const isLast = index === steps.length - 1
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<StepperStep
|
||||
{...step}
|
||||
activeIndex={activeIndex}
|
||||
index={index}
|
||||
/>
|
||||
{!isLast && <div className='h-px w-4 bg-divider-deep' />}
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
{steps.map((step, index) => {
|
||||
const isLast = index === steps.length - 1
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<StepperStep
|
||||
{...step}
|
||||
activeIndex={activeIndex}
|
||||
index={index}
|
||||
/>
|
||||
{!isLast && <div className="h-px w-4 bg-divider-deep" />}
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -15,27 +15,31 @@ export const StepperStep: FC<StepperStepProps> = (props) => {
|
||||
const isActive = index === activeIndex
|
||||
const isDisabled = activeIndex < index
|
||||
const label = isActive ? `STEP ${index + 1}` : `${index + 1}`
|
||||
return <div className='flex items-center gap-2'>
|
||||
<div className={cn('inline-flex h-5 flex-col items-center justify-center gap-2 rounded-3xl py-1',
|
||||
isActive
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={cn('inline-flex h-5 flex-col items-center justify-center gap-2 rounded-3xl py-1', isActive
|
||||
? 'bg-state-accent-solid px-2'
|
||||
: !isDisabled
|
||||
? 'w-5 border border-text-quaternary'
|
||||
: 'w-5 border border-divider-deep')}>
|
||||
<div className={cn('system-2xs-semibold-uppercase text-center',
|
||||
isActive
|
||||
? 'w-5 border border-text-quaternary'
|
||||
: 'w-5 border border-divider-deep')}
|
||||
>
|
||||
<div className={cn('system-2xs-semibold-uppercase text-center', isActive
|
||||
? 'text-text-primary-on-surface'
|
||||
: !isDisabled
|
||||
? 'text-text-tertiary'
|
||||
: 'text-text-quaternary')}>
|
||||
{label}
|
||||
? 'text-text-tertiary'
|
||||
: 'text-text-quaternary')}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn('system-xs-medium-uppercase',
|
||||
isActive
|
||||
<div className={cn('system-xs-medium-uppercase', isActive
|
||||
? 'system-xs-semibold-uppercase text-text-accent'
|
||||
: !isDisabled
|
||||
? 'text-text-tertiary'
|
||||
: 'text-text-quaternary')}>{name}</div>
|
||||
</div>
|
||||
? 'text-text-tertiary'
|
||||
: 'text-text-quaternary')}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './index.module.css'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import s from './index.module.css'
|
||||
|
||||
type IProps = {
|
||||
show: boolean
|
||||
@ -34,9 +34,9 @@ const StopEmbeddingModal = ({
|
||||
<span className={s.close} onClick={onHide} />
|
||||
<div className={s.title}>{t('datasetCreation.stepThree.modelTitle')}</div>
|
||||
<div className={s.content}>{t('datasetCreation.stepThree.modelContent')}</div>
|
||||
<div className='flex flex-row-reverse'>
|
||||
<Button className='ml-2 w-24' variant='primary' onClick={submit}>{t('datasetCreation.stepThree.modelButtonConfirm')}</Button>
|
||||
<Button className='w-24' onClick={onHide}>{t('datasetCreation.stepThree.modelButtonCancel')}</Button>
|
||||
<div className="flex flex-row-reverse">
|
||||
<Button className="ml-2 w-24" variant="primary" onClick={submit}>{t('datasetCreation.stepThree.modelButtonConfirm')}</Button>
|
||||
<Button className="w-24" onClick={onHide}>{t('datasetCreation.stepThree.modelButtonCancel')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import type { TopBarProps } from './index'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { TopBar, type TopBarProps } from './index'
|
||||
import { TopBar } from './index'
|
||||
|
||||
// Mock next/link to capture href values
|
||||
vi.mock('next/link', () => ({
|
||||
default: ({ children, href, replace, className }: { children: React.ReactNode; href: string; replace?: boolean; className?: string }) => (
|
||||
default: ({ children, href, replace, className }: { children: React.ReactNode, href: string, replace?: boolean, className?: string }) => (
|
||||
<a href={href} data-replace={replace} className={className} data-testid="back-link">
|
||||
{children}
|
||||
</a>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { type FC, useMemo } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import type { StepperProps } from '../stepper'
|
||||
import { RiArrowLeftLine } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Stepper, type StepperProps } from '../stepper'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { Stepper } from '../stepper'
|
||||
|
||||
export type TopBarProps = Pick<StepperProps, 'activeIndex'> & {
|
||||
className?: string
|
||||
@ -24,24 +26,24 @@ export const TopBar: FC<TopBarProps> = (props) => {
|
||||
return datasetId ? `/datasets/${datasetId}/documents` : '/datasets'
|
||||
}, [datasetId])
|
||||
|
||||
return <div className={cn('relative flex h-[52px] shrink-0 items-center justify-between border-b border-b-divider-subtle', className)}>
|
||||
<Link href={fallbackRoute} replace className="inline-flex h-12 items-center justify-start gap-1 py-2 pl-2 pr-6">
|
||||
<div className='p-2'>
|
||||
<RiArrowLeftLine className='size-4 text-text-primary' />
|
||||
return (
|
||||
<div className={cn('relative flex h-[52px] shrink-0 items-center justify-between border-b border-b-divider-subtle', className)}>
|
||||
<Link href={fallbackRoute} replace className="inline-flex h-12 items-center justify-start gap-1 py-2 pl-2 pr-6">
|
||||
<div className="p-2">
|
||||
<RiArrowLeftLine className="size-4 text-text-primary" />
|
||||
</div>
|
||||
<p className="system-sm-semibold-uppercase text-text-primary">
|
||||
{t('datasetCreation.steps.header.fallbackRoute')}
|
||||
</p>
|
||||
</Link>
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<Stepper
|
||||
steps={Array.from({ length: 3 }, (_, i) => ({
|
||||
name: t(STEP_T_MAP[i + 1]),
|
||||
}))}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
<p className="system-sm-semibold-uppercase text-text-primary">
|
||||
{t('datasetCreation.steps.header.fallbackRoute')}
|
||||
</p>
|
||||
</Link>
|
||||
<div className={
|
||||
'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'
|
||||
}>
|
||||
<Stepper
|
||||
steps={Array.from({ length: 3 }, (_, i) => ({
|
||||
name: t(STEP_T_MAP[i + 1]),
|
||||
}))}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import Input from './base/input'
|
||||
import Header from './base/header'
|
||||
import CrawledResult from './base/crawled-result'
|
||||
import CrawledResultItem from './base/crawled-result-item'
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
import Header from './base/header'
|
||||
import Input from './base/input'
|
||||
|
||||
// ============================================================================
|
||||
// Test Data Factories
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -31,9 +31,9 @@ const CheckboxWithLabel: FC<Props> = ({
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='w-[200px]'>{tooltip}</div>
|
||||
<div className="w-[200px]">{tooltip}</div>
|
||||
}
|
||||
triggerClassName='ml-0.5 w-4 h-4'
|
||||
triggerClassName="ml-0.5 w-4 h-4"
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
payload: CrawlResultItemType
|
||||
@ -31,19 +31,19 @@ const CrawledResultItem: FC<Props> = ({
|
||||
}, [isChecked, onCheckChange])
|
||||
return (
|
||||
<div className={cn(isPreview ? 'bg-state-base-active' : 'group hover:bg-state-base-hover', 'cursor-pointer rounded-lg p-2')}>
|
||||
<div className='relative flex'>
|
||||
<div className='flex h-5 items-center'>
|
||||
<Checkbox className='mr-2 shrink-0' checked={isChecked} onCheck={handleCheckChange} id={testId} />
|
||||
<div className="relative flex">
|
||||
<div className="flex h-5 items-center">
|
||||
<Checkbox className="mr-2 shrink-0" checked={isChecked} onCheck={handleCheckChange} id={testId} />
|
||||
</div>
|
||||
<div className='flex min-w-0 grow flex-col'>
|
||||
<div className="flex min-w-0 grow flex-col">
|
||||
<div
|
||||
className='truncate text-sm font-medium text-text-secondary'
|
||||
className="truncate text-sm font-medium text-text-secondary"
|
||||
title={payload.title}
|
||||
>
|
||||
{payload.title}
|
||||
</div>
|
||||
<div
|
||||
className='mt-0.5 truncate text-xs text-text-tertiary'
|
||||
className="mt-0.5 truncate text-xs text-text-tertiary"
|
||||
title={payload.source_url}
|
||||
>
|
||||
{payload.source_url}
|
||||
@ -51,7 +51,7 @@ const CrawledResultItem: FC<Props> = ({
|
||||
</div>
|
||||
<Button
|
||||
onClick={onPreview}
|
||||
className='right-0 top-0 hidden h-6 px-1.5 text-xs font-medium uppercase group-hover:absolute group-hover:block'
|
||||
className="right-0 top-0 hidden h-6 px-1.5 text-xs font-medium uppercase group-hover:absolute group-hover:block"
|
||||
>
|
||||
{t('datasetCreation.stepOne.website.preview')}
|
||||
</Button>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import CheckboxWithLabel from './checkbox-with-label'
|
||||
import CrawledResultItem from './crawled-result-item'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -58,22 +58,22 @@ const CrawledResult: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'border-t-[0.5px] border-divider-regular shadow-xs shadow-shadow-shadow-3')}>
|
||||
<div className='flex h-[34px] items-center justify-between px-4'>
|
||||
<div className="flex h-[34px] items-center justify-between px-4">
|
||||
<CheckboxWithLabel
|
||||
isChecked={isCheckAll}
|
||||
onChange={handleCheckedAll}
|
||||
label={isCheckAll ? t(`${I18N_PREFIX}.resetAll`) : t(`${I18N_PREFIX}.selectAll`)}
|
||||
labelClassName='system-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
testId='select-all'
|
||||
labelClassName="system-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
testId="select-all"
|
||||
/>
|
||||
<div className='text-xs text-text-tertiary'>
|
||||
<div className="text-xs text-text-tertiary">
|
||||
{t(`${I18N_PREFIX}.scrapTimeInfo`, {
|
||||
total: list.length,
|
||||
time: usedTime.toFixed(1),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-2'>
|
||||
<div className="p-2">
|
||||
{list.map((item, index) => (
|
||||
<CrawledResultItem
|
||||
key={item.source_url}
|
||||
|
||||
@ -19,15 +19,20 @@ const Crawling: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className='flex h-[34px] items-center border-y-[0.5px] border-divider-regular px-4
|
||||
text-xs text-text-tertiary shadow-xs shadow-shadow-shadow-3'>
|
||||
{t('datasetCreation.stepOne.website.totalPageScraped')} {crawledNum}/{totalNum}
|
||||
<div className="flex h-[34px] items-center border-y-[0.5px] border-divider-regular px-4
|
||||
text-xs text-text-tertiary shadow-xs shadow-shadow-shadow-3"
|
||||
>
|
||||
{t('datasetCreation.stepOne.website.totalPageScraped')}
|
||||
{' '}
|
||||
{crawledNum}
|
||||
/
|
||||
{totalNum}
|
||||
</div>
|
||||
|
||||
<div className='p-2'>
|
||||
<div className="p-2">
|
||||
{['', '', '', ''].map((item, index) => (
|
||||
<div className='py-[5px]' key={index}>
|
||||
<RowStruct className='text-text-quaternary' />
|
||||
<div className="py-[5px]" key={index}>
|
||||
<RowStruct className="text-text-quaternary" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -17,12 +17,12 @@ const ErrorMessage: FC<Props> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn(className, 'border-t border-divider-subtle bg-dataset-warning-message-bg px-4 py-2 opacity-40')}>
|
||||
<div className='flex h-5 items-center'>
|
||||
<AlertTriangle className='mr-2 h-4 w-4 text-text-warning-secondary' />
|
||||
<div className='system-md-medium text-text-warning'>{title}</div>
|
||||
<div className="flex h-5 items-center">
|
||||
<AlertTriangle className="mr-2 h-4 w-4 text-text-warning-secondary" />
|
||||
<div className="system-md-medium text-text-warning">{title}</div>
|
||||
</div>
|
||||
{errorMsg && (
|
||||
<div className='system-xs-regular mt-1 pl-6 text-text-secondary'>{errorMsg}</div>
|
||||
<div className="system-xs-regular mt-1 pl-6 text-text-secondary">{errorMsg}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import Input from './input'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Input from './input'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -30,15 +30,18 @@ const Field: FC<Props> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<div className='flex py-[7px]'>
|
||||
<div className={cn(labelClassName, 'flex h-[16px] items-center text-[13px] font-semibold text-text-secondary')}>{label} </div>
|
||||
{isRequired && <span className='ml-0.5 text-xs font-semibold text-text-destructive'>*</span>}
|
||||
<div className="flex py-[7px]">
|
||||
<div className={cn(labelClassName, 'flex h-[16px] items-center text-[13px] font-semibold text-text-secondary')}>
|
||||
{label}
|
||||
{' '}
|
||||
</div>
|
||||
{isRequired && <span className="ml-0.5 text-xs font-semibold text-text-destructive">*</span>}
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='w-[200px]'>{tooltip}</div>
|
||||
<div className="w-[200px]">{tooltip}</div>
|
||||
}
|
||||
triggerClassName='ml-0.5 w-4 h-4'
|
||||
triggerClassName="ml-0.5 w-4 h-4"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react'
|
||||
import React from 'react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type HeaderProps = {
|
||||
isInPipeline?: boolean
|
||||
@ -22,37 +22,38 @@ const Header = ({
|
||||
docLink,
|
||||
}: HeaderProps) => {
|
||||
return (
|
||||
<div className='flex items-center gap-x-2'>
|
||||
<div className='flex shrink-0 grow items-center gap-x-1'>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="flex shrink-0 grow items-center gap-x-1">
|
||||
<div className={cn(
|
||||
'text-text-secondary',
|
||||
isInPipeline ? 'system-sm-semibold' : 'system-md-semibold',
|
||||
)}>
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
<Divider type='vertical' className='mx-1 h-3.5' />
|
||||
<Divider type="vertical" className="mx-1 h-3.5" />
|
||||
<Button
|
||||
variant='secondary'
|
||||
size='small'
|
||||
variant="secondary"
|
||||
size="small"
|
||||
className={cn(isInPipeline ? 'size-6 px-1' : 'gap-x-0.5 px-1.5')}
|
||||
onClick={onClickConfiguration}
|
||||
>
|
||||
<RiEqualizer2Line className='size-4' />
|
||||
<RiEqualizer2Line className="size-4" />
|
||||
{!isInPipeline && (
|
||||
<span className='system-xs-medium'>
|
||||
<span className="system-xs-medium">
|
||||
{buttonText}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<a
|
||||
className='system-xs-medium flex items-center gap-x-1 overflow-hidden text-text-accent'
|
||||
className="system-xs-medium flex items-center gap-x-1 overflow-hidden text-text-accent"
|
||||
href={docLink}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<RiBookOpenLine className='size-3.5 shrink-0' />
|
||||
<span className='grow truncate' title={docTitle}>{docTitle}</span>
|
||||
<RiBookOpenLine className="size-3.5 shrink-0" />
|
||||
<span className="grow truncate" title={docTitle}>{docTitle}</span>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -50,12 +50,12 @@ const Input: FC<Props> = ({
|
||||
{...otherOption}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className='system-xs-regular focus:bg-components-inout-border-active flex h-8 w-full rounded-lg border border-transparent
|
||||
className="system-xs-regular focus:bg-components-inout-border-active flex h-8 w-full rounded-lg border border-transparent
|
||||
bg-components-input-bg-normal p-2 text-components-input-text-filled
|
||||
caret-[#295eff] placeholder:text-components-input-text-placeholder hover:border
|
||||
hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border focus:border-components-input-border-active
|
||||
focus:shadow-xs focus:shadow-shadow-shadow-3
|
||||
focus-visible:outline-none'
|
||||
focus-visible:outline-none"
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
'use client'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import type { FC } from 'react'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
type Props = {
|
||||
@ -33,17 +34,17 @@ const OptionsWrap: FC<Props> = ({
|
||||
return (
|
||||
<div className={cn(className, !fold ? 'mb-0' : 'mb-3')}>
|
||||
<div
|
||||
className='flex h-[26px] cursor-pointer select-none items-center gap-x-1 py-1'
|
||||
className="flex h-[26px] cursor-pointer select-none items-center gap-x-1 py-1"
|
||||
onClick={foldToggle}
|
||||
>
|
||||
<div className='flex grow items-center'>
|
||||
<RiEqualizer2Line className='mr-1 h-4 w-4 text-text-secondary' />
|
||||
<span className='text-[13px] font-semibold uppercase leading-[16px] text-text-secondary'>{t(`${I18N_PREFIX}.options`)}</span>
|
||||
<div className="flex grow items-center">
|
||||
<RiEqualizer2Line className="mr-1 h-4 w-4 text-text-secondary" />
|
||||
<span className="text-[13px] font-semibold uppercase leading-[16px] text-text-secondary">{t(`${I18N_PREFIX}.options`)}</span>
|
||||
</div>
|
||||
<ChevronRight className={cn(!fold && 'rotate-90', 'h-4 w-4 shrink-0 text-text-tertiary')} />
|
||||
</div>
|
||||
{!fold && (
|
||||
<div className='mb-4'>
|
||||
<div className="mb-4">
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from './input'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import Input from './input'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -30,17 +30,17 @@ const UrlInput: FC<Props> = ({
|
||||
}, [isRunning, onRun, url])
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-between gap-x-2'>
|
||||
<div className="flex items-center justify-between gap-x-2">
|
||||
<Input
|
||||
value={url}
|
||||
onChange={handleUrlChange}
|
||||
placeholder={docLink()}
|
||||
/>
|
||||
<Button
|
||||
variant='primary'
|
||||
variant="primary"
|
||||
onClick={handleOnRun}
|
||||
loading={isRunning}
|
||||
spinnerClassName='!ml-0'
|
||||
spinnerClassName="!ml-0"
|
||||
>
|
||||
{!isRunning ? t(`${I18N_PREFIX}.run`) : ''}
|
||||
</Button>
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import UrlInput from '../base/url-input'
|
||||
import OptionsWrap from '../base/options-wrap'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
import { checkFirecrawlTaskStatus, createFirecrawlTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import CrawledResult from '../base/crawled-result'
|
||||
import Crawling from '../base/crawling'
|
||||
import ErrorMessage from '../base/error-message'
|
||||
import Options from './options'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { checkFirecrawlTaskStatus, createFirecrawlTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import Header from '../base/header'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import OptionsWrap from '../base/options-wrap'
|
||||
import UrlInput from '../base/url-input'
|
||||
import Options from './options'
|
||||
|
||||
const ERROR_I18N_PREFIX = 'common.errorMsg'
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
@ -187,38 +187,41 @@ const FireCrawl: FC<Props> = ({
|
||||
title={t(`${I18N_PREFIX}.firecrawlTitle`)}
|
||||
buttonText={t(`${I18N_PREFIX}.configureFirecrawl`)}
|
||||
docTitle={t(`${I18N_PREFIX}.firecrawlDoc`)}
|
||||
docLink={'https://docs.firecrawl.dev/introduction'}
|
||||
docLink="https://docs.firecrawl.dev/introduction"
|
||||
/>
|
||||
<div className='mt-2 rounded-xl border border-components-panel-border bg-background-default-subtle p-4 pb-0'>
|
||||
<div className="mt-2 rounded-xl border border-components-panel-border bg-background-default-subtle p-4 pb-0">
|
||||
<UrlInput onRun={handleRun} isRunning={isRunning} />
|
||||
<OptionsWrap
|
||||
className='mt-4'
|
||||
className="mt-4"
|
||||
controlFoldOptions={controlFoldOptions}
|
||||
>
|
||||
<Options className='mt-2' payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
<Options className="mt-2" payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
</OptionsWrap>
|
||||
|
||||
{!isInit && (
|
||||
<div className='relative left-[-16px] mt-3 w-[calc(100%_+_32px)] rounded-b-xl'>
|
||||
<div className="relative left-[-16px] mt-3 w-[calc(100%_+_32px)] rounded-b-xl">
|
||||
{isRunning
|
||||
&& <Crawling
|
||||
className='mt-2'
|
||||
crawledNum={crawlResult?.current || 0}
|
||||
totalNum={crawlResult?.total || Number.parseFloat(crawlOptions.limit as string) || 0}
|
||||
/>}
|
||||
&& (
|
||||
<Crawling
|
||||
className="mt-2"
|
||||
crawledNum={crawlResult?.current || 0}
|
||||
totalNum={crawlResult?.total || Number.parseFloat(crawlOptions.limit as string) || 0}
|
||||
/>
|
||||
)}
|
||||
{showError && (
|
||||
<ErrorMessage className='rounded-b-xl' title={t(`${I18N_PREFIX}.exceptionErrorTitle`)} errorMsg={crawlErrorMessage} />
|
||||
<ErrorMessage className="rounded-b-xl" title={t(`${I18N_PREFIX}.exceptionErrorTitle`)} errorMsg={crawlErrorMessage} />
|
||||
)}
|
||||
{isCrawlFinished && !showError
|
||||
&& <CrawledResult
|
||||
className='mb-2'
|
||||
list={crawlResult?.data || []}
|
||||
checkedList={checkedCrawlResult}
|
||||
onSelectedChange={onCheckedCrawlResultChange}
|
||||
onPreview={onPreview}
|
||||
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
|
||||
/>
|
||||
}
|
||||
&& (
|
||||
<CrawledResult
|
||||
className="mb-2"
|
||||
list={crawlResult?.data || []}
|
||||
checkedList={checkedCrawlResult}
|
||||
onSelectedChange={onCheckedCrawlResultChange}
|
||||
onPreview={onPreview}
|
||||
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlOptions } from '@/models/datasets'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import CheckboxWithLabel from '../base/checkbox-with-label'
|
||||
import Field from '../base/field'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CrawlOptions } from '@/models/datasets'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -36,11 +36,11 @@ const Options: FC<Props> = ({
|
||||
label={t(`${I18N_PREFIX}.crawlSubPage`)}
|
||||
isChecked={payload.crawl_sub_pages}
|
||||
onChange={handleChange('crawl_sub_pages')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
labelClassName="text-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
/>
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.limit`)}
|
||||
value={payload.limit}
|
||||
onChange={handleChange('limit')}
|
||||
@ -48,7 +48,7 @@ const Options: FC<Props> = ({
|
||||
isRequired
|
||||
/>
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.maxDepth`)}
|
||||
value={payload.max_depth}
|
||||
onChange={handleChange('max_depth')}
|
||||
@ -57,27 +57,27 @@ const Options: FC<Props> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.excludePaths`)}
|
||||
value={payload.excludes}
|
||||
onChange={handleChange('excludes')}
|
||||
placeholder='blog/*, /about/*'
|
||||
placeholder="blog/*, /about/*"
|
||||
/>
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.includeOnlyPaths`)}
|
||||
value={payload.includes}
|
||||
onChange={handleChange('includes')}
|
||||
placeholder='articles/*'
|
||||
placeholder="articles/*"
|
||||
/>
|
||||
</div>
|
||||
<CheckboxWithLabel
|
||||
label={t(`${I18N_PREFIX}.extractOnlyMainContent`)}
|
||||
isChecked={payload.only_main_content}
|
||||
onChange={handleChange('only_main_content')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
labelClassName="text-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './index.module.css'
|
||||
import NoData from './no-data'
|
||||
import Firecrawl from './firecrawl'
|
||||
import Watercrawl from './watercrawl'
|
||||
import JinaReader from './jina-reader'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import { DataSourceProvider } from '@/models/common'
|
||||
import { ENABLE_WEBSITE_FIRECRAWL, ENABLE_WEBSITE_JINAREADER, ENABLE_WEBSITE_WATERCRAWL } from '@/config'
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { ENABLE_WEBSITE_FIRECRAWL, ENABLE_WEBSITE_JINAREADER, ENABLE_WEBSITE_WATERCRAWL } from '@/config'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { DataSourceProvider } from '@/models/common'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Firecrawl from './firecrawl'
|
||||
import s from './index.module.css'
|
||||
import JinaReader from './jina-reader'
|
||||
import NoData from './no-data'
|
||||
import Watercrawl from './watercrawl'
|
||||
|
||||
type Props = {
|
||||
onPreview: (payload: CrawlResultItem) => void
|
||||
@ -44,7 +44,8 @@ const Website: FC<Props> = ({
|
||||
return [
|
||||
DataSourceProvider.jinaReader,
|
||||
DataSourceProvider.fireCrawl,
|
||||
DataSourceProvider.waterCrawl].includes(item.provider as DataSourceProvider) && item.credentials_list.length > 0
|
||||
DataSourceProvider.waterCrawl,
|
||||
].includes(item.provider as DataSourceProvider) && item.credentials_list.length > 0
|
||||
}), [authedDataSourceList])
|
||||
|
||||
const handleOnConfig = useCallback(() => {
|
||||
@ -57,55 +58,58 @@ const Website: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='mb-4'>
|
||||
<div className='system-md-medium mb-2 text-text-secondary'>
|
||||
<div className="mb-4">
|
||||
<div className="system-md-medium mb-2 text-text-secondary">
|
||||
{t('datasetCreation.stepOne.website.chooseProvider')}
|
||||
</div>
|
||||
<div className='flex space-x-2'>
|
||||
{ENABLE_WEBSITE_JINAREADER && <button type="button"
|
||||
className={cn('flex items-center justify-center rounded-lg px-4 py-2',
|
||||
selectedProvider === DataSourceProvider.jinaReader
|
||||
<div className="flex space-x-2">
|
||||
{ENABLE_WEBSITE_JINAREADER && (
|
||||
<button
|
||||
type="button"
|
||||
className={cn('flex items-center justify-center rounded-lg px-4 py-2', selectedProvider === DataSourceProvider.jinaReader
|
||||
? 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary'
|
||||
: `system-sm-regular border border-components-option-card-option-border bg-components-option-card-option-bg text-text-secondary
|
||||
hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs hover:shadow-shadow-shadow-3`,
|
||||
)}
|
||||
onClick={() => {
|
||||
setSelectedProvider(DataSourceProvider.jinaReader)
|
||||
onCrawlProviderChange(DataSourceProvider.jinaReader)
|
||||
}}
|
||||
>
|
||||
<span className={cn(s.jinaLogo, 'mr-2')} />
|
||||
<span>Jina Reader</span>
|
||||
</button>}
|
||||
{ENABLE_WEBSITE_FIRECRAWL && <button type="button"
|
||||
className={cn('rounded-lg px-4 py-2',
|
||||
selectedProvider === DataSourceProvider.fireCrawl
|
||||
hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs hover:shadow-shadow-shadow-3`)}
|
||||
onClick={() => {
|
||||
setSelectedProvider(DataSourceProvider.jinaReader)
|
||||
onCrawlProviderChange(DataSourceProvider.jinaReader)
|
||||
}}
|
||||
>
|
||||
<span className={cn(s.jinaLogo, 'mr-2')} />
|
||||
<span>Jina Reader</span>
|
||||
</button>
|
||||
)}
|
||||
{ENABLE_WEBSITE_FIRECRAWL && (
|
||||
<button
|
||||
type="button"
|
||||
className={cn('rounded-lg px-4 py-2', selectedProvider === DataSourceProvider.fireCrawl
|
||||
? 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary'
|
||||
: `system-sm-regular border border-components-option-card-option-border bg-components-option-card-option-bg text-text-secondary
|
||||
hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs hover:shadow-shadow-shadow-3`,
|
||||
)}
|
||||
onClick={() => {
|
||||
setSelectedProvider(DataSourceProvider.fireCrawl)
|
||||
onCrawlProviderChange(DataSourceProvider.fireCrawl)
|
||||
}}
|
||||
>
|
||||
🔥 Firecrawl
|
||||
</button>}
|
||||
{ENABLE_WEBSITE_WATERCRAWL && <button type="button"
|
||||
className={cn('flex items-center justify-center rounded-lg px-4 py-2',
|
||||
selectedProvider === DataSourceProvider.waterCrawl
|
||||
hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs hover:shadow-shadow-shadow-3`)}
|
||||
onClick={() => {
|
||||
setSelectedProvider(DataSourceProvider.fireCrawl)
|
||||
onCrawlProviderChange(DataSourceProvider.fireCrawl)
|
||||
}}
|
||||
>
|
||||
🔥 Firecrawl
|
||||
</button>
|
||||
)}
|
||||
{ENABLE_WEBSITE_WATERCRAWL && (
|
||||
<button
|
||||
type="button"
|
||||
className={cn('flex items-center justify-center rounded-lg px-4 py-2', selectedProvider === DataSourceProvider.waterCrawl
|
||||
? 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary'
|
||||
: `system-sm-regular border border-components-option-card-option-border bg-components-option-card-option-bg text-text-secondary
|
||||
hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs hover:shadow-shadow-shadow-3`,
|
||||
)}
|
||||
onClick={() => {
|
||||
setSelectedProvider(DataSourceProvider.waterCrawl)
|
||||
onCrawlProviderChange(DataSourceProvider.waterCrawl)
|
||||
}}
|
||||
>
|
||||
<span className={cn(s.watercrawlLogo, 'mr-2')} />
|
||||
<span>WaterCrawl</span>
|
||||
</button>}
|
||||
hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs hover:shadow-shadow-shadow-3`)}
|
||||
onClick={() => {
|
||||
setSelectedProvider(DataSourceProvider.waterCrawl)
|
||||
onCrawlProviderChange(DataSourceProvider.waterCrawl)
|
||||
}}
|
||||
>
|
||||
<span className={cn(s.watercrawlLogo, 'mr-2')} />
|
||||
<span>WaterCrawl</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{source && selectedProvider === DataSourceProvider.fireCrawl && (
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '../../base/input'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import Input from '../../base/input'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -30,18 +30,18 @@ const UrlInput: FC<Props> = ({
|
||||
}, [isRunning, onRun, url])
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className="flex items-center justify-between">
|
||||
<Input
|
||||
value={url}
|
||||
onChange={handleUrlChange}
|
||||
placeholder={docLink()}
|
||||
/>
|
||||
<Button
|
||||
variant='primary'
|
||||
variant="primary"
|
||||
onClick={handleOnRun}
|
||||
className='ml-2'
|
||||
className="ml-2"
|
||||
loading={isRunning}
|
||||
data-testid='url-input-run-button'
|
||||
data-testid="url-input-run-button"
|
||||
>
|
||||
{!isRunning ? t(`${I18N_PREFIX}.run`) : ''}
|
||||
</Button>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import JinaReader from './index'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import { checkJinaReaderTaskStatus, createJinaReaderTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import JinaReader from './index'
|
||||
|
||||
// Mock external dependencies
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
@ -748,8 +748,7 @@ describe('JinaReader', () => {
|
||||
current: 100,
|
||||
total: 100,
|
||||
data: Array.from({ length: 100 }, (_, i) =>
|
||||
createCrawlResultItem({ source_url: `https://example.com/${i}` }),
|
||||
),
|
||||
createCrawlResultItem({ source_url: `https://example.com/${i}` })),
|
||||
})
|
||||
|
||||
const props = createDefaultProps({
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import UrlInput from '../base/url-input'
|
||||
import OptionsWrap from '../base/options-wrap'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { checkJinaReaderTaskStatus, createJinaReaderTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import CrawledResult from '../base/crawled-result'
|
||||
import Crawling from '../base/crawling'
|
||||
import ErrorMessage from '../base/error-message'
|
||||
import Options from './options'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { checkJinaReaderTaskStatus, createJinaReaderTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import Header from '../base/header'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import OptionsWrap from '../base/options-wrap'
|
||||
import UrlInput from '../base/url-input'
|
||||
import Options from './options'
|
||||
|
||||
const ERROR_I18N_PREFIX = 'common.errorMsg'
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
@ -197,38 +197,41 @@ const JinaReader: FC<Props> = ({
|
||||
title={t(`${I18N_PREFIX}.jinaReaderTitle`)}
|
||||
buttonText={t(`${I18N_PREFIX}.configureJinaReader`)}
|
||||
docTitle={t(`${I18N_PREFIX}.jinaReaderDoc`)}
|
||||
docLink={'https://jina.ai/reader'}
|
||||
docLink="https://jina.ai/reader"
|
||||
/>
|
||||
<div className='mt-2 rounded-xl border border-components-panel-border bg-background-default-subtle p-4 pb-0'>
|
||||
<div className="mt-2 rounded-xl border border-components-panel-border bg-background-default-subtle p-4 pb-0">
|
||||
<UrlInput onRun={handleRun} isRunning={isRunning} />
|
||||
<OptionsWrap
|
||||
className='mt-4'
|
||||
className="mt-4"
|
||||
controlFoldOptions={controlFoldOptions}
|
||||
>
|
||||
<Options className='mt-2' payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
<Options className="mt-2" payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
</OptionsWrap>
|
||||
|
||||
{!isInit && (
|
||||
<div className='relative left-[-16px] mt-3 w-[calc(100%_+_32px)] rounded-b-xl'>
|
||||
<div className="relative left-[-16px] mt-3 w-[calc(100%_+_32px)] rounded-b-xl">
|
||||
{isRunning
|
||||
&& <Crawling
|
||||
className='mt-2'
|
||||
crawledNum={crawlResult?.current || 0}
|
||||
totalNum={crawlResult?.total || Number.parseFloat(crawlOptions.limit as string) || 0}
|
||||
/>}
|
||||
&& (
|
||||
<Crawling
|
||||
className="mt-2"
|
||||
crawledNum={crawlResult?.current || 0}
|
||||
totalNum={crawlResult?.total || Number.parseFloat(crawlOptions.limit as string) || 0}
|
||||
/>
|
||||
)}
|
||||
{showError && (
|
||||
<ErrorMessage className='rounded-b-xl' title={t(`${I18N_PREFIX}.exceptionErrorTitle`)} errorMsg={crawlErrorMessage} />
|
||||
<ErrorMessage className="rounded-b-xl" title={t(`${I18N_PREFIX}.exceptionErrorTitle`)} errorMsg={crawlErrorMessage} />
|
||||
)}
|
||||
{isCrawlFinished && !showError
|
||||
&& <CrawledResult
|
||||
className='mb-2'
|
||||
list={crawlResult?.data || []}
|
||||
checkedList={checkedCrawlResult}
|
||||
onSelectedChange={onCheckedCrawlResultChange}
|
||||
onPreview={onPreview}
|
||||
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
|
||||
/>
|
||||
}
|
||||
&& (
|
||||
<CrawledResult
|
||||
className="mb-2"
|
||||
list={crawlResult?.data || []}
|
||||
checkedList={checkedCrawlResult}
|
||||
onSelectedChange={onCheckedCrawlResultChange}
|
||||
onPreview={onPreview}
|
||||
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlOptions } from '@/models/datasets'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import CheckboxWithLabel from '../base/checkbox-with-label'
|
||||
import Field from '../base/field'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CrawlOptions } from '@/models/datasets'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -36,20 +36,20 @@ const Options: FC<Props> = ({
|
||||
label={t(`${I18N_PREFIX}.crawlSubPage`)}
|
||||
isChecked={payload.crawl_sub_pages}
|
||||
onChange={handleChange('crawl_sub_pages')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
testId='crawl-sub-pages'
|
||||
labelClassName="text-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
testId="crawl-sub-pages"
|
||||
/>
|
||||
<CheckboxWithLabel
|
||||
label={t(`${I18N_PREFIX}.useSitemap`)}
|
||||
isChecked={payload.use_sitemap}
|
||||
onChange={handleChange('use_sitemap')}
|
||||
tooltip={t(`${I18N_PREFIX}.useSitemapTooltip`) as string}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
testId='use-sitemap'
|
||||
labelClassName="text-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
testId="use-sitemap"
|
||||
/>
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.limit`)}
|
||||
value={payload.limit}
|
||||
onChange={handleChange('limit')}
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './index.module.css'
|
||||
import { Icon3Dots } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { Icon3Dots } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { ENABLE_WEBSITE_FIRECRAWL, ENABLE_WEBSITE_JINAREADER, ENABLE_WEBSITE_WATERCRAWL } from '@/config'
|
||||
import { DataSourceProvider } from '@/models/common'
|
||||
import s from './index.module.css'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -26,44 +26,52 @@ const NoData: FC<Props> = ({
|
||||
title: string
|
||||
description: string
|
||||
} | null> = {
|
||||
[DataSourceProvider.jinaReader]: ENABLE_WEBSITE_JINAREADER ? {
|
||||
emoji: <span className={s.jinaLogo} />,
|
||||
title: t(`${I18N_PREFIX}.jinaReaderNotConfigured`),
|
||||
description: t(`${I18N_PREFIX}.jinaReaderNotConfiguredDescription`),
|
||||
} : null,
|
||||
[DataSourceProvider.fireCrawl]: ENABLE_WEBSITE_FIRECRAWL ? {
|
||||
emoji: '🔥',
|
||||
title: t(`${I18N_PREFIX}.fireCrawlNotConfigured`),
|
||||
description: t(`${I18N_PREFIX}.fireCrawlNotConfiguredDescription`),
|
||||
} : null,
|
||||
[DataSourceProvider.waterCrawl]: ENABLE_WEBSITE_WATERCRAWL ? {
|
||||
emoji: '💧',
|
||||
title: t(`${I18N_PREFIX}.waterCrawlNotConfigured`),
|
||||
description: t(`${I18N_PREFIX}.waterCrawlNotConfiguredDescription`),
|
||||
} : null,
|
||||
[DataSourceProvider.jinaReader]: ENABLE_WEBSITE_JINAREADER
|
||||
? {
|
||||
emoji: <span className={s.jinaLogo} />,
|
||||
title: t(`${I18N_PREFIX}.jinaReaderNotConfigured`),
|
||||
description: t(`${I18N_PREFIX}.jinaReaderNotConfiguredDescription`),
|
||||
}
|
||||
: null,
|
||||
[DataSourceProvider.fireCrawl]: ENABLE_WEBSITE_FIRECRAWL
|
||||
? {
|
||||
emoji: '🔥',
|
||||
title: t(`${I18N_PREFIX}.fireCrawlNotConfigured`),
|
||||
description: t(`${I18N_PREFIX}.fireCrawlNotConfiguredDescription`),
|
||||
}
|
||||
: null,
|
||||
[DataSourceProvider.waterCrawl]: ENABLE_WEBSITE_WATERCRAWL
|
||||
? {
|
||||
emoji: '💧',
|
||||
title: t(`${I18N_PREFIX}.waterCrawlNotConfigured`),
|
||||
description: t(`${I18N_PREFIX}.waterCrawlNotConfiguredDescription`),
|
||||
}
|
||||
: null,
|
||||
}
|
||||
|
||||
const currentProvider = providerConfig[provider] || providerConfig.jinareader
|
||||
|
||||
if (!currentProvider) return null
|
||||
if (!currentProvider)
|
||||
return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='mt-4 max-w-[640px] rounded-2xl bg-workflow-process-bg p-6'>
|
||||
<div className='flex h-12 w-12 items-center justify-center rounded-[10px] border-[0.5px]
|
||||
border-components-card-border bg-components-card-bg shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'>
|
||||
<div className="mt-4 max-w-[640px] rounded-2xl bg-workflow-process-bg p-6">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-[10px] border-[0.5px]
|
||||
border-components-card-border bg-components-card-bg shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]"
|
||||
>
|
||||
{currentProvider.emoji}
|
||||
</div>
|
||||
<div className='mb-1 mt-2 flex flex-col gap-y-1 pb-3 pt-1'>
|
||||
<span className='system-md-semibold text-text-secondary'>
|
||||
<div className="mb-1 mt-2 flex flex-col gap-y-1 pb-3 pt-1">
|
||||
<span className="system-md-semibold text-text-secondary">
|
||||
{currentProvider.title}
|
||||
<Icon3Dots className='relative -left-1.5 -top-2.5 inline' />
|
||||
<Icon3Dots className="relative -left-1.5 -top-2.5 inline" />
|
||||
</span>
|
||||
<div className='system-sm-regular text-text-tertiary'>
|
||||
<div className="system-sm-regular text-text-tertiary">
|
||||
{currentProvider.description}
|
||||
</div>
|
||||
</div>
|
||||
<Button variant='primary' onClick={onConfig}>
|
||||
<Button variant="primary" onClick={onConfig}>
|
||||
{t(`${I18N_PREFIX}.configure`)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import s from '../file-preview/index.module.css'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
import s from '../file-preview/index.module.css'
|
||||
|
||||
type IProps = {
|
||||
payload: CrawlResultItem
|
||||
@ -22,14 +22,14 @@ const WebsitePreview = ({
|
||||
<div className={cn(s.previewHeader)}>
|
||||
<div className={cn(s.title, 'title-md-semi-bold')}>
|
||||
<span>{t('datasetCreation.stepOne.pagePreview')}</span>
|
||||
<div className='flex h-6 w-6 cursor-pointer items-center justify-center' onClick={hidePreview}>
|
||||
<XMarkIcon className='h-4 w-4'></XMarkIcon>
|
||||
<div className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={hidePreview}>
|
||||
<XMarkIcon className="h-4 w-4"></XMarkIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div className='title-sm-semi-bold break-words text-text-primary'>
|
||||
<div className="title-sm-semi-bold break-words text-text-primary">
|
||||
{payload.title}
|
||||
</div>
|
||||
<div className='system-xs-medium truncate text-text-tertiary' title={payload.source_url}>{payload.source_url}</div>
|
||||
<div className="system-xs-medium truncate text-text-tertiary" title={payload.source_url}>{payload.source_url}</div>
|
||||
</div>
|
||||
<div className={cn(s.previewContent, 'body-md-regular')}>
|
||||
<div className={cn(s.fileContent)}>{payload.markdown}</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import WaterCrawl from './index'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import { checkWatercrawlTaskStatus, createWatercrawlTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import WaterCrawl from './index'
|
||||
|
||||
// Mock external dependencies
|
||||
vi.mock('@/service/datasets', () => ({
|
||||
@ -759,8 +759,7 @@ describe('WaterCrawl', () => {
|
||||
current: 100,
|
||||
total: 100,
|
||||
data: Array.from({ length: 100 }, (_, i) =>
|
||||
createCrawlResultItem({ source_url: `https://example.com/${i}` }),
|
||||
),
|
||||
createCrawlResultItem({ source_url: `https://example.com/${i}` })),
|
||||
})
|
||||
|
||||
const props = createDefaultProps({
|
||||
@ -1615,16 +1614,14 @@ describe('WaterCrawl', () => {
|
||||
current: 5,
|
||||
total: 10,
|
||||
data: Array.from({ length: 5 }, (_, i) =>
|
||||
createCrawlResultItem({ source_url: `https://page${i + 1}.com` }),
|
||||
),
|
||||
createCrawlResultItem({ source_url: `https://page${i + 1}.com` })),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
status: 'completed',
|
||||
current: 10,
|
||||
total: 10,
|
||||
data: Array.from({ length: 10 }, (_, i) =>
|
||||
createCrawlResultItem({ source_url: `https://page${i + 1}.com` }),
|
||||
),
|
||||
createCrawlResultItem({ source_url: `https://page${i + 1}.com` })),
|
||||
})
|
||||
|
||||
const props = createDefaultProps({
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import UrlInput from '../base/url-input'
|
||||
import OptionsWrap from '../base/options-wrap'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { checkWatercrawlTaskStatus, createWatercrawlTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import CrawledResult from '../base/crawled-result'
|
||||
import Crawling from '../base/crawling'
|
||||
import ErrorMessage from '../base/error-message'
|
||||
import Options from './options'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { checkWatercrawlTaskStatus, createWatercrawlTask } from '@/service/datasets'
|
||||
import { sleep } from '@/utils'
|
||||
import Header from '../base/header'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import OptionsWrap from '../base/options-wrap'
|
||||
import UrlInput from '../base/url-input'
|
||||
import Options from './options'
|
||||
|
||||
const ERROR_I18N_PREFIX = 'common.errorMsg'
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
@ -183,38 +183,41 @@ const WaterCrawl: FC<Props> = ({
|
||||
title={t(`${I18N_PREFIX}.watercrawlTitle`)}
|
||||
buttonText={t(`${I18N_PREFIX}.configureWatercrawl`)}
|
||||
docTitle={t(`${I18N_PREFIX}.watercrawlDoc`)}
|
||||
docLink={'https://docs.watercrawl.dev/'}
|
||||
docLink="https://docs.watercrawl.dev/"
|
||||
/>
|
||||
<div className='mt-2 rounded-xl border border-components-panel-border bg-background-default-subtle p-4 pb-0'>
|
||||
<div className="mt-2 rounded-xl border border-components-panel-border bg-background-default-subtle p-4 pb-0">
|
||||
<UrlInput onRun={handleRun} isRunning={isRunning} />
|
||||
<OptionsWrap
|
||||
className='mt-4'
|
||||
className="mt-4"
|
||||
controlFoldOptions={controlFoldOptions}
|
||||
>
|
||||
<Options className='mt-2' payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
<Options className="mt-2" payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
</OptionsWrap>
|
||||
|
||||
{!isInit && (
|
||||
<div className='relative left-[-16px] mt-3 w-[calc(100%_+_32px)] rounded-b-xl'>
|
||||
<div className="relative left-[-16px] mt-3 w-[calc(100%_+_32px)] rounded-b-xl">
|
||||
{isRunning
|
||||
&& <Crawling
|
||||
className='mt-2'
|
||||
crawledNum={crawlResult?.current || 0}
|
||||
totalNum={crawlResult?.total || Number.parseFloat(crawlOptions.limit as string) || 0}
|
||||
/>}
|
||||
&& (
|
||||
<Crawling
|
||||
className="mt-2"
|
||||
crawledNum={crawlResult?.current || 0}
|
||||
totalNum={crawlResult?.total || Number.parseFloat(crawlOptions.limit as string) || 0}
|
||||
/>
|
||||
)}
|
||||
{showError && (
|
||||
<ErrorMessage className='rounded-b-xl' title={t(`${I18N_PREFIX}.exceptionErrorTitle`)} errorMsg={crawlErrorMessage} />
|
||||
<ErrorMessage className="rounded-b-xl" title={t(`${I18N_PREFIX}.exceptionErrorTitle`)} errorMsg={crawlErrorMessage} />
|
||||
)}
|
||||
{isCrawlFinished && !showError
|
||||
&& <CrawledResult
|
||||
className='mb-2'
|
||||
list={crawlResult?.data || []}
|
||||
checkedList={checkedCrawlResult}
|
||||
onSelectedChange={onCheckedCrawlResultChange}
|
||||
onPreview={onPreview}
|
||||
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
|
||||
/>
|
||||
}
|
||||
&& (
|
||||
<CrawledResult
|
||||
className="mb-2"
|
||||
list={crawlResult?.data || []}
|
||||
checkedList={checkedCrawlResult}
|
||||
onSelectedChange={onCheckedCrawlResultChange}
|
||||
onPreview={onPreview}
|
||||
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { CrawlOptions } from '@/models/datasets'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import CheckboxWithLabel from '../base/checkbox-with-label'
|
||||
import Field from '../base/field'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import type { CrawlOptions } from '@/models/datasets'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -36,12 +36,12 @@ const Options: FC<Props> = ({
|
||||
label={t(`${I18N_PREFIX}.crawlSubPage`)}
|
||||
isChecked={payload.crawl_sub_pages}
|
||||
onChange={handleChange('crawl_sub_pages')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
testId='crawl-sub-pages'
|
||||
labelClassName="text-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
testId="crawl-sub-pages"
|
||||
/>
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.limit`)}
|
||||
value={payload.limit}
|
||||
onChange={handleChange('limit')}
|
||||
@ -49,7 +49,7 @@ const Options: FC<Props> = ({
|
||||
isRequired
|
||||
/>
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.maxDepth`)}
|
||||
value={payload.max_depth}
|
||||
onChange={handleChange('max_depth')}
|
||||
@ -58,28 +58,28 @@ const Options: FC<Props> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.excludePaths`)}
|
||||
value={payload.excludes}
|
||||
onChange={handleChange('excludes')}
|
||||
placeholder='blog/*, /about/*'
|
||||
placeholder="blog/*, /about/*"
|
||||
/>
|
||||
<Field
|
||||
className='shrink-0 grow'
|
||||
className="shrink-0 grow"
|
||||
label={t(`${I18N_PREFIX}.includeOnlyPaths`)}
|
||||
value={payload.includes}
|
||||
onChange={handleChange('includes')}
|
||||
placeholder='articles/*'
|
||||
placeholder="articles/*"
|
||||
/>
|
||||
</div>
|
||||
<CheckboxWithLabel
|
||||
label={t(`${I18N_PREFIX}.extractOnlyMainContent`)}
|
||||
isChecked={payload.only_main_content}
|
||||
onChange={handleChange('only_main_content')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
testId='only-main-content'
|
||||
labelClassName="text-[13px] leading-[16px] font-medium text-text-secondary"
|
||||
testId="only-main-content"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user