mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 04:26:30 +08:00
refactor: enhance document preview functionality and refactor form handling
This commit is contained in:
parent
5ac1e3584d
commit
29d2f2339b
@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import DataSourceOptions from './data-source-options'
|
import DataSourceOptions from './data-source-options'
|
||||||
import type { CrawlResultItem, CustomFile as File, FileIndexingEstimateResponse, FileItem } from '@/models/datasets'
|
import type { CrawlResultItem, DocumentItem, CustomFile as File, FileIndexingEstimateResponse, FileItem } from '@/models/datasets'
|
||||||
import LocalFile from '@/app/components/rag-pipeline/components/panel/test-run/data-source/local-file'
|
import LocalFile from '@/app/components/rag-pipeline/components/panel/test-run/data-source/local-file'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { useProviderContextSelector } from '@/context/provider-context'
|
import { useProviderContextSelector } from '@/context/provider-context'
|
||||||
@ -48,6 +48,9 @@ const TestRunPanel = () => {
|
|||||||
|
|
||||||
const isPreview = useRef(false)
|
const isPreview = useRef(false)
|
||||||
const formRef = useRef<any>(null)
|
const formRef = useRef<any>(null)
|
||||||
|
const previewFile = useRef<DocumentItem>(fileList[0].file as DocumentItem)
|
||||||
|
const previewNotionPage = useRef<NotionPage>(notionPages[0])
|
||||||
|
const previewWebsitePage = useRef<CrawlResultItem>(websitePages[0])
|
||||||
|
|
||||||
const { data: pipelineInfo, isFetching: isFetchingPipelineInfo } = usePublishedPipelineInfo(pipelineId || '')
|
const { data: pipelineInfo, isFetching: isFetchingPipelineInfo } = usePublishedPipelineInfo(pipelineId || '')
|
||||||
|
|
||||||
@ -132,7 +135,7 @@ const TestRunPanel = () => {
|
|||||||
return
|
return
|
||||||
const datasourceInfoList: Record<string, any>[] = []
|
const datasourceInfoList: Record<string, any>[] = []
|
||||||
if (datasource.type === DatasourceType.localFile) {
|
if (datasource.type === DatasourceType.localFile) {
|
||||||
const { id, name, type, size, extension, mime_type } = fileList[0].file
|
const { id, name, type, size, extension, mime_type } = previewFile.current as File
|
||||||
const documentInfo = {
|
const documentInfo = {
|
||||||
upload_file_id: id,
|
upload_file_id: id,
|
||||||
name,
|
name,
|
||||||
@ -144,7 +147,7 @@ const TestRunPanel = () => {
|
|||||||
datasourceInfoList.push(documentInfo)
|
datasourceInfoList.push(documentInfo)
|
||||||
}
|
}
|
||||||
if (datasource.type === DatasourceType.onlineDocument) {
|
if (datasource.type === DatasourceType.onlineDocument) {
|
||||||
const { workspace_id, ...rest } = notionPages[0]
|
const { workspace_id, ...rest } = previewNotionPage.current
|
||||||
const documentInfo = {
|
const documentInfo = {
|
||||||
workspace_id,
|
workspace_id,
|
||||||
page: rest,
|
page: rest,
|
||||||
@ -154,7 +157,7 @@ const TestRunPanel = () => {
|
|||||||
if (datasource.type === DatasourceType.websiteCrawl) {
|
if (datasource.type === DatasourceType.websiteCrawl) {
|
||||||
const documentInfo = {
|
const documentInfo = {
|
||||||
job_id: websiteCrawlJobId,
|
job_id: websiteCrawlJobId,
|
||||||
result: websitePages[0],
|
result: previewWebsitePage.current,
|
||||||
}
|
}
|
||||||
datasourceInfoList.push(documentInfo)
|
datasourceInfoList.push(documentInfo)
|
||||||
}
|
}
|
||||||
@ -167,10 +170,10 @@ const TestRunPanel = () => {
|
|||||||
is_preview: true,
|
is_preview: true,
|
||||||
}, {
|
}, {
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
setEstimateData(res as FileIndexingEstimateResponse)
|
setEstimateData(res.data.outputs as FileIndexingEstimateResponse)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, [datasource, fileList, notionPages, pipelineId, runPublishedPipeline, websiteCrawlJobId, websitePages])
|
}, [datasource, pipelineId, runPublishedPipeline, websiteCrawlJobId])
|
||||||
|
|
||||||
const handleProcess = useCallback(async (data: Record<string, any>) => {
|
const handleProcess = useCallback(async (data: Record<string, any>) => {
|
||||||
if (!datasource)
|
if (!datasource)
|
||||||
@ -231,14 +234,25 @@ const TestRunPanel = () => {
|
|||||||
formRef.current?.submit()
|
formRef.current?.submit()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onClickReset = useCallback(() => {
|
|
||||||
formRef.current?.reset()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleSubmit = useCallback((data: Record<string, any>) => {
|
const handleSubmit = useCallback((data: Record<string, any>) => {
|
||||||
isPreview.current ? handlePreviewChunks(data) : handleProcess(data)
|
isPreview.current ? handlePreviewChunks(data) : handleProcess(data)
|
||||||
}, [handlePreviewChunks, handleProcess])
|
}, [handlePreviewChunks, handleProcess])
|
||||||
|
|
||||||
|
const handlePreviewFileChange = useCallback((file: DocumentItem) => {
|
||||||
|
previewFile.current = file
|
||||||
|
onClickPreview()
|
||||||
|
}, [onClickPreview])
|
||||||
|
|
||||||
|
const handlePreviewNotionPageChange = useCallback((page: NotionPage) => {
|
||||||
|
previewNotionPage.current = page
|
||||||
|
onClickPreview()
|
||||||
|
}, [onClickPreview])
|
||||||
|
|
||||||
|
const handlePreviewWebsiteChange = useCallback((website: CrawlResultItem) => {
|
||||||
|
previewWebsitePage.current = website
|
||||||
|
onClickPreview()
|
||||||
|
}, [onClickPreview])
|
||||||
|
|
||||||
if (isFetchingPipelineInfo) {
|
if (isFetchingPipelineInfo) {
|
||||||
return (
|
return (
|
||||||
<Loading type='app' />
|
<Loading type='app' />
|
||||||
@ -312,7 +326,6 @@ const TestRunPanel = () => {
|
|||||||
onProcess={onClickProcess}
|
onProcess={onClickProcess}
|
||||||
onPreview={onClickPreview}
|
onPreview={onClickPreview}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onReset={onClickReset}
|
|
||||||
onBack={handleBackStep}
|
onBack={handleBackStep}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -353,6 +366,9 @@ const TestRunPanel = () => {
|
|||||||
isPending={isPending}
|
isPending={isPending}
|
||||||
estimateData={estimateData}
|
estimateData={estimateData}
|
||||||
onPreview={onClickPreview}
|
onPreview={onClickPreview}
|
||||||
|
handlePreviewFileChange={handlePreviewFileChange}
|
||||||
|
handlePreviewNotionPageChange={handlePreviewNotionPageChange}
|
||||||
|
handlePreviewWebsitePageChange={handlePreviewWebsiteChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -26,6 +26,9 @@ type ChunkPreviewProps = {
|
|||||||
isPending: boolean
|
isPending: boolean
|
||||||
estimateData: FileIndexingEstimateResponse | undefined
|
estimateData: FileIndexingEstimateResponse | undefined
|
||||||
onPreview: () => void
|
onPreview: () => void
|
||||||
|
handlePreviewFileChange: (file: DocumentItem) => void
|
||||||
|
handlePreviewNotionPageChange: (page: NotionPage) => void
|
||||||
|
handlePreviewWebsitePageChange: (page: CrawlResultItem) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChunkPreview = ({
|
const ChunkPreview = ({
|
||||||
@ -37,6 +40,9 @@ const ChunkPreview = ({
|
|||||||
isPending,
|
isPending,
|
||||||
estimateData,
|
estimateData,
|
||||||
onPreview,
|
onPreview,
|
||||||
|
handlePreviewFileChange,
|
||||||
|
handlePreviewNotionPageChange,
|
||||||
|
handlePreviewWebsitePageChange,
|
||||||
}: ChunkPreviewProps) => {
|
}: ChunkPreviewProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const currentDocForm = useDatasetDetailContextWithSelector(s => s.dataset?.doc_form)
|
const currentDocForm = useDatasetDetailContextWithSelector(s => s.dataset?.doc_form)
|
||||||
@ -58,6 +64,7 @@ const ChunkPreview = ({
|
|||||||
files={files as Array<Required<CustomFile>>}
|
files={files as Array<Required<CustomFile>>}
|
||||||
onChange={(selected) => {
|
onChange={(selected) => {
|
||||||
setPreviewFile(selected)
|
setPreviewFile(selected)
|
||||||
|
handlePreviewFileChange(selected)
|
||||||
}}
|
}}
|
||||||
value={previewFile}
|
value={previewFile}
|
||||||
/>
|
/>
|
||||||
@ -74,6 +81,7 @@ const ChunkPreview = ({
|
|||||||
onChange={(selected) => {
|
onChange={(selected) => {
|
||||||
const selectedPage = notionPages.find(page => page.page_id === selected.id)
|
const selectedPage = notionPages.find(page => page.page_id === selected.id)
|
||||||
setPreviewNotionPage(selectedPage!)
|
setPreviewNotionPage(selectedPage!)
|
||||||
|
handlePreviewNotionPageChange(selectedPage!)
|
||||||
}}
|
}}
|
||||||
value={{
|
value={{
|
||||||
id: previewNotionPage?.page_id || '',
|
id: previewNotionPage?.page_id || '',
|
||||||
@ -94,6 +102,7 @@ const ChunkPreview = ({
|
|||||||
onChange={(selected) => {
|
onChange={(selected) => {
|
||||||
const selectedPage = websitePages.find(page => page.source_url === selected.id)
|
const selectedPage = websitePages.find(page => page.source_url === selected.id)
|
||||||
setPreviewWebsitePage(selectedPage!)
|
setPreviewWebsitePage(selectedPage!)
|
||||||
|
handlePreviewWebsitePageChange(selectedPage!)
|
||||||
}}
|
}}
|
||||||
value={
|
value={
|
||||||
{
|
{
|
||||||
@ -113,7 +122,7 @@ const ChunkPreview = ({
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</PreviewHeader>}
|
</PreviewHeader>}
|
||||||
className='relative flex h-full w-1/2 shrink-0 p-4 pr-0'
|
className='relative flex h-full w-full shrink-0'
|
||||||
mainClassName='space-y-6'
|
mainClassName='space-y-6'
|
||||||
>
|
>
|
||||||
{currentDocForm === ChunkingMode.qa && estimateData?.qa_preview && (
|
{currentDocForm === ChunkingMode.qa && estimateData?.qa_preview && (
|
||||||
@ -169,7 +178,7 @@ const ChunkPreview = ({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
{!isIdle && (
|
{isIdle && (
|
||||||
<div className='flex h-full w-full items-center justify-center'>
|
<div className='flex h-full w-full items-center justify-center'>
|
||||||
<div className='flex flex-col items-center justify-center gap-3 pb-4'>
|
<div className='flex flex-col items-center justify-center gap-3 pb-4'>
|
||||||
<RiSearchEyeLine className='size-10 text-text-empty-state-icon' />
|
<RiSearchEyeLine className='size-10 text-text-empty-state-icon' />
|
||||||
|
|||||||
@ -2,22 +2,25 @@ import { useAppForm } from '@/app/components/base/form'
|
|||||||
import BaseField from '@/app/components/base/form/form-scenarios/base/field'
|
import BaseField from '@/app/components/base/form/form-scenarios/base/field'
|
||||||
import type { BaseConfiguration } from '@/app/components/base/form/form-scenarios/base/types'
|
import type { BaseConfiguration } from '@/app/components/base/form/form-scenarios/base/types'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { useImperativeHandle } from 'react'
|
import { useCallback, useImperativeHandle } from 'react'
|
||||||
import type { ZodSchema } from 'zod'
|
import type { ZodSchema } from 'zod'
|
||||||
|
import Header from './header'
|
||||||
|
|
||||||
type OptionsProps = {
|
type OptionsProps = {
|
||||||
initialData: Record<string, any>
|
initialData: Record<string, any>
|
||||||
configurations: BaseConfiguration[]
|
configurations: BaseConfiguration[]
|
||||||
schema: ZodSchema
|
schema: ZodSchema
|
||||||
onSubmit: (data: Record<string, any>) => void
|
onSubmit: (data: Record<string, any>) => void
|
||||||
|
onPreview: () => void
|
||||||
ref: React.RefObject<any>
|
ref: React.RefObject<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Options = ({
|
const Form = ({
|
||||||
initialData,
|
initialData,
|
||||||
configurations,
|
configurations,
|
||||||
schema,
|
schema,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
onPreview,
|
||||||
ref,
|
ref,
|
||||||
}: OptionsProps) => {
|
}: OptionsProps) => {
|
||||||
const form = useAppForm({
|
const form = useAppForm({
|
||||||
@ -48,24 +51,32 @@ const Options = ({
|
|||||||
submit: () => {
|
submit: () => {
|
||||||
form.handleSubmit()
|
form.handleSubmit()
|
||||||
},
|
},
|
||||||
reset: () => {
|
|
||||||
form.reset()
|
|
||||||
},
|
|
||||||
isDirty: () => {
|
|
||||||
return form.state.isDirty
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}, [form])
|
}, [form])
|
||||||
|
|
||||||
|
const handleReset = useCallback(() => {
|
||||||
|
form.reset()
|
||||||
|
}, [form])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className='w-full'
|
className='flex w-full flex-col rounded-lg border border-components-panel-border bg-components-panel-bg'
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
form.handleSubmit()
|
form.handleSubmit()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<form.Subscribe
|
||||||
|
selector={state => state.isDirty}
|
||||||
|
children={isDirty => (
|
||||||
|
<Header
|
||||||
|
onReset={handleReset}
|
||||||
|
resetDisabled={!isDirty}
|
||||||
|
onPreview={onPreview}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className='flex flex-col gap-3 border-t border-divider-subtle px-4 py-3'>
|
<div className='flex flex-col gap-3 border-t border-divider-subtle px-4 py-3'>
|
||||||
{configurations.map((config, index) => {
|
{configurations.map((config, index) => {
|
||||||
const FieldComponent = BaseField({
|
const FieldComponent = BaseField({
|
||||||
@ -79,4 +90,4 @@ const Options = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Options
|
export default Form
|
||||||
@ -5,13 +5,13 @@ import { RiSearchEyeLine } from '@remixicon/react'
|
|||||||
|
|
||||||
type HeaderProps = {
|
type HeaderProps = {
|
||||||
onReset: () => void
|
onReset: () => void
|
||||||
disableReset?: boolean
|
resetDisabled: boolean
|
||||||
onPreview?: () => void
|
onPreview?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header = ({
|
const Header = ({
|
||||||
onReset,
|
onReset,
|
||||||
disableReset = true,
|
resetDisabled,
|
||||||
onPreview,
|
onPreview,
|
||||||
}: HeaderProps) => {
|
}: HeaderProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -21,7 +21,7 @@ const Header = ({
|
|||||||
<div className='system-sm-semibold-uppercase grow text-text-secondary'>
|
<div className='system-sm-semibold-uppercase grow text-text-secondary'>
|
||||||
{t('datasetPipeline.addDocuments.stepTwo.chunkSettings')}
|
{t('datasetPipeline.addDocuments.stepTwo.chunkSettings')}
|
||||||
</div>
|
</div>
|
||||||
<Button variant='ghost' disabled={disableReset} onClick={onReset}>
|
<Button variant='ghost' disabled={resetDisabled} onClick={onReset}>
|
||||||
{t('common.operation.reset')}
|
{t('common.operation.reset')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
import { generateZodSchema } from '@/app/components/base/form/form-scenarios/base/utils'
|
import { generateZodSchema } from '@/app/components/base/form/form-scenarios/base/utils'
|
||||||
import { useConfigurations } from './hooks'
|
import { useConfigurations } from './hooks'
|
||||||
import Options from './options'
|
import Form from './form'
|
||||||
import Actions from './actions'
|
import Actions from './actions'
|
||||||
import Header from './header'
|
|
||||||
|
|
||||||
type ProcessDocumentsProps = {
|
type ProcessDocumentsProps = {
|
||||||
dataSourceNodeId: string
|
dataSourceNodeId: string
|
||||||
ref: React.RefObject<any>
|
ref: React.RefObject<any>
|
||||||
onProcess: () => void
|
onProcess: () => void
|
||||||
onPreview: () => void
|
onPreview: () => void
|
||||||
onReset: () => void
|
|
||||||
onSubmit: (data: Record<string, any>) => void
|
onSubmit: (data: Record<string, any>) => void
|
||||||
onBack: () => void
|
onBack: () => void
|
||||||
}
|
}
|
||||||
@ -19,7 +17,6 @@ const ProcessDocuments = ({
|
|||||||
onProcess,
|
onProcess,
|
||||||
onPreview,
|
onPreview,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onReset,
|
|
||||||
onBack,
|
onBack,
|
||||||
ref,
|
ref,
|
||||||
}: ProcessDocumentsProps) => {
|
}: ProcessDocumentsProps) => {
|
||||||
@ -28,20 +25,14 @@ const ProcessDocuments = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-y-4 pt-4'>
|
<div className='flex flex-col gap-y-4 pt-4'>
|
||||||
<div className='flex flex-col rounded-lg border border-components-panel-border bg-components-panel-bg'>
|
<Form
|
||||||
<Header
|
ref={ref}
|
||||||
onReset={onReset}
|
initialData={initialData}
|
||||||
disableReset={!ref.current?.isDirty()}
|
configurations={configurations}
|
||||||
onPreview={onPreview}
|
schema={schema}
|
||||||
/>
|
onSubmit={onSubmit}
|
||||||
<Options
|
onPreview={onPreview}
|
||||||
ref={ref}
|
/>
|
||||||
initialData={initialData}
|
|
||||||
configurations={configurations}
|
|
||||||
schema={schema}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Actions onBack={onBack} onProcess={onProcess} />
|
<Actions onBack={onBack} onProcess={onProcess} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user