dify/web/app/components/workflow/update-dsl-modal.tsx
zxhlyh 7a1d6fe509
Feat/attachments (#9526)
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: JzoNg <jzongcode@gmail.com>
2024-10-21 10:32:37 +08:00

183 lines
6.2 KiB
TypeScript

'use client'
import type { MouseEventHandler } from 'react'
import {
memo,
useCallback,
useRef,
useState,
} from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import {
RiAlertLine,
RiCloseLine,
} from '@remixicon/react'
import { WORKFLOW_DATA_UPDATE } from './constants'
import {
SupportUploadFileTypes,
} from './types'
import {
initialEdges,
initialNodes,
} from './utils'
import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
import Button from '@/app/components/base/button'
import Modal from '@/app/components/base/modal'
import { ToastContext } from '@/app/components/base/toast'
import { updateWorkflowDraftFromDSL } from '@/service/workflow'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useStore as useAppStore } from '@/app/components/app/store'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
type UpdateDSLModalProps = {
onCancel: () => void
onBackup: () => void
onImport?: () => void
}
const UpdateDSLModal = ({
onCancel,
onBackup,
onImport,
}: UpdateDSLModalProps) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const appDetail = useAppStore(s => s.appDetail)
const [currentFile, setDSLFile] = useState<File>()
const [fileContent, setFileContent] = useState<string>()
const [loading, setLoading] = useState(false)
const { eventEmitter } = useEventEmitterContextContext()
const readFile = (file: File) => {
const reader = new FileReader()
reader.onload = function (event) {
const content = event.target?.result
setFileContent(content as string)
}
reader.readAsText(file)
}
const handleFile = (file?: File) => {
setDSLFile(file)
if (file)
readFile(file)
if (!file)
setFileContent('')
}
const isCreatingRef = useRef(false)
const handleImport: MouseEventHandler = useCallback(async () => {
if (isCreatingRef.current)
return
isCreatingRef.current = true
if (!currentFile)
return
try {
if (appDetail && fileContent) {
setLoading(true)
const {
graph,
features,
hash,
} = await updateWorkflowDraftFromDSL(appDetail.id, fileContent)
const { nodes, edges, viewport } = graph
const newFeatures = {
file: {
image: {
enabled: !!features.file_upload?.image?.enabled,
number_limits: features.file_upload?.image?.number_limits || 3,
transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
},
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3,
},
opening: {
enabled: !!features.opening_statement,
opening_statement: features.opening_statement,
suggested_questions: features.suggested_questions,
},
suggested: features.suggested_questions_after_answer || { enabled: false },
speech2text: features.speech_to_text || { enabled: false },
text2speech: features.text_to_speech || { enabled: false },
citation: features.retriever_resource || { enabled: false },
moderation: features.sensitive_word_avoidance || { enabled: false },
}
eventEmitter?.emit({
type: WORKFLOW_DATA_UPDATE,
payload: {
nodes: initialNodes(nodes, edges),
edges: initialEdges(edges, nodes),
viewport,
features: newFeatures,
hash,
},
} as any)
if (onImport)
onImport()
notify({ type: 'success', message: t('workflow.common.importSuccess') })
setLoading(false)
onCancel()
}
}
catch (e) {
setLoading(false)
notify({ type: 'error', message: t('workflow.common.importFailure') })
}
isCreatingRef.current = false
}, [currentFile, fileContent, onCancel, notify, t, eventEmitter, appDetail, onImport])
return (
<Modal
className='p-6 w-[520px] rounded-2xl'
isShow={true}
onClose={() => {}}
>
<div className='flex items-center justify-between mb-6'>
<div className='text-2xl font-semibold text-[#101828]'>{t('workflow.common.importDSL')}</div>
<div className='flex items-center justify-center w-[22px] h-[22px] cursor-pointer' onClick={onCancel}>
<RiCloseLine className='w-5 h-5 text-gray-500' />
</div>
</div>
<div className='flex mb-4 px-4 py-3 bg-[#FFFAEB] rounded-xl border border-[#FEDF89]'>
<RiAlertLine className='shrink-0 mt-0.5 mr-2 w-4 h-4 text-[#F79009]' />
<div>
<div className='mb-2 text-sm font-medium text-[#354052]'>{t('workflow.common.importDSLTip')}</div>
<Button
variant='secondary-accent'
onClick={onBackup}
>
{t('workflow.common.backupCurrentDraft')}
</Button>
</div>
</div>
<div className='mb-8'>
<div className='mb-1 text-[13px] font-semibold text-[#354052]'>
{t('workflow.common.chooseDSL')}
</div>
<Uploader
file={currentFile}
updateFile={handleFile}
className='!mt-0'
/>
</div>
<div className='flex justify-end'>
<Button className='mr-2' onClick={onCancel}>{t('app.newApp.Cancel')}</Button>
<Button
disabled={!currentFile || loading}
variant='warning'
onClick={handleImport}
loading={loading}
>
{t('workflow.common.overwriteAndImport')}
</Button>
</div>
</Modal>
)
}
export default memo(UpdateDSLModal)